refactored massive language file, separating into language_types and bytecode
tests for parser and virtualmachine now work! Running test.moo is now able to successfully call built-in functions --HG-- branch : mung
This commit is contained in:
parent
bae69502bd
commit
dcaeb7145f
9 changed files with 993 additions and 995 deletions
|
@ -8,3 +8,4 @@ class builtin_functions(object):
|
||||||
print "serverlog: %s" % (args,)
|
print "serverlog: %s" % (args,)
|
||||||
|
|
||||||
bi = builtin_functions()
|
bi = builtin_functions()
|
||||||
|
|
||||||
|
|
739
bytecode.py
739
bytecode.py
|
@ -0,0 +1,739 @@
|
||||||
|
from language_tools import *
|
||||||
|
from builtins import builtin_map
|
||||||
|
import optimizer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def coerce(value):
|
||||||
|
from language_types import *
|
||||||
|
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)
|
||||||
|
elif isinstance(value, VMType):
|
||||||
|
return value
|
||||||
|
else:
|
||||||
|
raise TypeError("Unknown type %s cannot be coerced to VMType" % (type(value),))
|
||||||
|
|
||||||
|
|
||||||
|
def uncoerce(value):
|
||||||
|
from language_types import VMType
|
||||||
|
assert isinstance(value, VMType)
|
||||||
|
return value.value
|
||||||
|
|
||||||
|
|
||||||
|
def codejoin(*args):
|
||||||
|
rv = []
|
||||||
|
for arg in args:
|
||||||
|
if isinstance(arg, CodeOpSequence):
|
||||||
|
for subarg in arg.sequence:
|
||||||
|
rv.extend(codejoin(subarg))
|
||||||
|
elif isinstance(arg, list):
|
||||||
|
for subarg in arg:
|
||||||
|
rv.extend(codejoin(subarg))
|
||||||
|
else:
|
||||||
|
rv.append(arg)
|
||||||
|
|
||||||
|
return rv
|
||||||
|
seq = CodeOpSequence.new(rv)
|
||||||
|
return seq
|
||||||
|
#return flatten(rv, ltypes=(list,))
|
||||||
|
|
||||||
|
|
||||||
|
class CodeOp(VMBaseObject):
|
||||||
|
def load_stack(self, vm):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def ticks(self):
|
||||||
|
return 1
|
||||||
|
|
||||||
|
class CodeOpSequence(CodeOp):
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def new(input):
|
||||||
|
c = CodeOpSequence()
|
||||||
|
c.sequence = input
|
||||||
|
return c
|
||||||
|
|
||||||
|
def load_stack(self, vm):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def ticks(self):
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
class StackCodeOp(CodeOp):
|
||||||
|
def __init__(self, stack_value):
|
||||||
|
CodeOp.__init__(self)
|
||||||
|
self.stack_value = stack_value
|
||||||
|
|
||||||
|
def load_stack(self, vm):
|
||||||
|
vm.push(self.stack_value)
|
||||||
|
|
||||||
|
|
||||||
|
class NoOp(CodeOp):
|
||||||
|
def execute(self, vm):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def ticks(self):
|
||||||
|
return 0
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
@tokenparser
|
||||||
|
def parse(tokens):
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
class GetProperty(CodeOp):
|
||||||
|
def execute(self, vm):
|
||||||
|
prop, obj = vm.pop(2)
|
||||||
|
vm.push(vm.db.get_property(obj, prop))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
@tokenparser
|
||||||
|
def parse(tokens):
|
||||||
|
return codejoin(GetProperty())
|
||||||
|
|
||||||
|
class SetProperty(CodeOp):
|
||||||
|
def execute(self, vm):
|
||||||
|
val, prop, obj = vm.pop(3)
|
||||||
|
vm.db.set_property(obj, prop, val)
|
||||||
|
vm.push(val)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
@tokenparser
|
||||||
|
def parse(tokens):
|
||||||
|
return codejoin(StackLiteral(tokens[0]))
|
||||||
|
|
||||||
|
class GetFile(CodeOp):
|
||||||
|
def execute(self, vm):
|
||||||
|
prop, obj = vm.pop(2)
|
||||||
|
vm.push(vm.db.get_file(obj, prop))
|
||||||
|
|
||||||
|
class SetFile(CodeOp):
|
||||||
|
def execute(self, vm):
|
||||||
|
val, prop, obj = vm.pop(3)
|
||||||
|
vm.db.set_file(obj, prop, val)
|
||||||
|
vm.push(val)
|
||||||
|
|
||||||
|
class SetVariable(CodeOp):
|
||||||
|
def execute(self, vm):
|
||||||
|
val, varname = vm.pop(2)
|
||||||
|
vm.setvar(varname, val)
|
||||||
|
vm.push(val)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
@tokenparser
|
||||||
|
def parse(tokens):
|
||||||
|
assert isinstance(tokens[0], GetVariable)
|
||||||
|
return codejoin(SetVariable(tokens[0].stack_value))
|
||||||
|
|
||||||
|
|
||||||
|
class GetVariable(CodeOp):
|
||||||
|
def execute(self, vm):
|
||||||
|
varname, = vm.pop(1)
|
||||||
|
vm.push(vm.getvar(varname))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
@tokenparser
|
||||||
|
def parse(tokens):
|
||||||
|
return codejoin(tokens[0], GetVariable())
|
||||||
|
|
||||||
|
class CallBuiltin(CodeOp):
|
||||||
|
def execute(self, 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(tokens[0], tokens[1], CallBuiltin())
|
||||||
|
|
||||||
|
class CallFunction(CodeOp):
|
||||||
|
def execute(self, vm):
|
||||||
|
obj, name = vm.pop(2)
|
||||||
|
vm.code_push(codejoin(StartContext(), vm.get_code(obj, name), EndContext()))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
@tokenparser
|
||||||
|
def parse(tokens):
|
||||||
|
return codejoin(tokens[0], CallFunction())
|
||||||
|
|
||||||
|
class ArithAdd(CodeOp):
|
||||||
|
def execute(self, vm):
|
||||||
|
v1, v2 = vm.pop(2)
|
||||||
|
vm.push(v1+v2)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
@tokenparser
|
||||||
|
def parse(tokens):
|
||||||
|
if tokens[0] == "+":
|
||||||
|
return codejoin(tokens[1], ArithAdd())
|
||||||
|
else:
|
||||||
|
return codejoin(tokens[1], ArithSub())
|
||||||
|
|
||||||
|
class ArithSub(CodeOp):
|
||||||
|
def execute(self, vm):
|
||||||
|
v1, v2 = vm.pop(2)
|
||||||
|
vm.push(v1-v2)
|
||||||
|
|
||||||
|
|
||||||
|
class ArithMul(CodeOp):
|
||||||
|
def execute(self, vm):
|
||||||
|
v1, v2 = vm.pop(2)
|
||||||
|
vm.push(v1*v2)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
@tokenparser
|
||||||
|
def parse(tokens):
|
||||||
|
if tokens[0] == "*":
|
||||||
|
return codejoin(tokens[1], ArithMul())
|
||||||
|
else:
|
||||||
|
return codejoin(tokens[1], ArithDiv())
|
||||||
|
|
||||||
|
class ArithDiv(CodeOp):
|
||||||
|
def execute(self, vm):
|
||||||
|
v1, v2 = vm.pop(2)
|
||||||
|
vm.push(v1/v2)
|
||||||
|
|
||||||
|
class ArithExp(CodeOp):
|
||||||
|
def execute(self, vm):
|
||||||
|
v1, v2 = vm.pop(2)
|
||||||
|
vm.push(v1**v2)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
@tokenparser
|
||||||
|
def parse(tokens):
|
||||||
|
return codejoin(tokens[1], ArithExp())
|
||||||
|
|
||||||
|
|
||||||
|
class BoolCompare(CodeOp):
|
||||||
|
map = {
|
||||||
|
'==': lambda x, y: int(x==y),
|
||||||
|
'!=': lambda x, y: int(x!=y),
|
||||||
|
'>': lambda x, y: int(x>y),
|
||||||
|
'<': lambda x, y: int(x<y),
|
||||||
|
'<=': lambda x, y: int(x<=y),
|
||||||
|
'>=': lambda x, y: int(x>=y),
|
||||||
|
'in': lambda x, y: int(x in y)
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self, cmpfunc):
|
||||||
|
CodeOp.__init__(self)
|
||||||
|
self.cmp = cmpfunc
|
||||||
|
|
||||||
|
def execute(self, vm):
|
||||||
|
v1, v2 = vm.pop(2)
|
||||||
|
vm.push(self.cmp(v1, v2))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
@tokenparser
|
||||||
|
def parse(tokens):
|
||||||
|
op = tokens[0]
|
||||||
|
|
||||||
|
return codejoin(tokens[1], BoolCompare(BoolCompare.map[op]))
|
||||||
|
|
||||||
|
|
||||||
|
class BoolLogic(CodeOp):
|
||||||
|
map = {
|
||||||
|
'&&': lambda x, y: int(x and y),
|
||||||
|
'||': lambda x, y: int(x or y),
|
||||||
|
'~~': lambda x, y: int((x or y) and not (x and y)),
|
||||||
|
'and': lambda x, y: int(x and y),
|
||||||
|
'or': lambda x, y: int(x or y),
|
||||||
|
'xor': lambda x, y: int((x or y) and not (x and y)),
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self, cmpfunc):
|
||||||
|
CodeOp.__init__(self)
|
||||||
|
self.cmp = cmpfunc
|
||||||
|
|
||||||
|
def execute(self, vm):
|
||||||
|
v1, v2 = vm.pop(2)
|
||||||
|
vm.push(self.cmp(v1, v2))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
@tokenparser
|
||||||
|
def parse(tokens):
|
||||||
|
op = tokens[0]
|
||||||
|
|
||||||
|
return codejoin(tokens[1], BoolLogic(BoolLogic.map[op]))
|
||||||
|
|
||||||
|
class UnaryOp(CodeOp):
|
||||||
|
map = {
|
||||||
|
"!": lambda x: int(not x),
|
||||||
|
"-": lambda x: -x
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self, cmpfunc):
|
||||||
|
CodeOp.__init__(self)
|
||||||
|
self.cmp = cmpfunc
|
||||||
|
|
||||||
|
def execute(self, vm):
|
||||||
|
v = vm.pop(1)
|
||||||
|
vm.push(self.cmp(v))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
@tokenparser
|
||||||
|
def parse(tokens):
|
||||||
|
rv = []
|
||||||
|
ops = []
|
||||||
|
for t in tokens:
|
||||||
|
if isinstance(t, (str, unicode)) and t in UnaryOp.map:
|
||||||
|
ops.append(UnaryOp(UnaryOp.map[t]))
|
||||||
|
else:
|
||||||
|
rv.append(t)
|
||||||
|
return codejoin(rv, ops)
|
||||||
|
|
||||||
|
class Assignment(CodeOp):
|
||||||
|
@staticmethod
|
||||||
|
@tokenparser
|
||||||
|
def parse(tokens):
|
||||||
|
from language_types import VMVariable, VMPropRef, VMFileRef
|
||||||
|
if len(tokens) > 2:
|
||||||
|
assert tokens[1] == "="
|
||||||
|
var = tokens[0]
|
||||||
|
if isinstance(var, VMVariable):
|
||||||
|
var = var.ref() + [SetVariable()]
|
||||||
|
elif isinstance(var, VMPropRef):
|
||||||
|
var = var.ref() + [SetProperty()]
|
||||||
|
elif isinstance(var, VMFileRef):
|
||||||
|
var = var.ref() + [SetFile()]
|
||||||
|
else:
|
||||||
|
raise ValueError, "Assignment to unknown type: %s" % (var,)
|
||||||
|
|
||||||
|
return codejoin(tokens[2:], var)
|
||||||
|
return tokens
|
||||||
|
|
||||||
|
class StackLiteral(StackCodeOp):
|
||||||
|
def execute(self, vm):
|
||||||
|
vm.push(self.stack_value)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
@tokenparser
|
||||||
|
def parse(tokens):
|
||||||
|
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),)
|
||||||
|
|
||||||
|
|
||||||
|
class StackToList(CodeOp):
|
||||||
|
def execute(self, vm):
|
||||||
|
from language_types import VMList
|
||||||
|
count, = vm.pop(1)
|
||||||
|
stacklist = vm.pop_raw(count)
|
||||||
|
i = 0
|
||||||
|
while i < len(stacklist):
|
||||||
|
if isinstance(stacklist[i], VMList) and stacklist[i].flat:
|
||||||
|
nexti = i + len(stacklist[i])
|
||||||
|
stacklist[i:i+1] = uncoerce(stacklist[i])
|
||||||
|
else:
|
||||||
|
stacklist[i] = uncoerce(stacklist[i])
|
||||||
|
nexti = i + 1
|
||||||
|
|
||||||
|
i = nexti
|
||||||
|
vm.push(stacklist)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
@tokenparser
|
||||||
|
def parse(tokens):
|
||||||
|
if not tokens:
|
||||||
|
return codejoin(StackLiteral(0), StackToList())
|
||||||
|
rv = codejoin(tokens[0][0], StackLiteral(len(tokens)), StackToList())
|
||||||
|
return rv
|
||||||
|
|
||||||
|
class Flatten(CodeOp):
|
||||||
|
def execute(self, vm):
|
||||||
|
flatlist, = vm.pop_raw(1)
|
||||||
|
flatlist.flat = 1
|
||||||
|
vm.push(flatlist)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
@tokenparser
|
||||||
|
def parse(tokens):
|
||||||
|
if len(tokens) > 0 and tokens[0] == '@':
|
||||||
|
return codejoin(tokens[0], Flatten())
|
||||||
|
else:
|
||||||
|
return tokens
|
||||||
|
|
||||||
|
|
||||||
|
class CallFunc(CodeOp):
|
||||||
|
def execute(self, vm):
|
||||||
|
obj, funcname, args = vm.pop(3)
|
||||||
|
vm.push_context(args)
|
||||||
|
vm.push_code(codejoin(
|
||||||
|
vm.get_code(obj, funcname),
|
||||||
|
EndContext()
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
@tokenparser
|
||||||
|
def parse(tokens):
|
||||||
|
assert len(tokens) == 4 and tokens[1] == ":"
|
||||||
|
return codejoin(tokens[0], tokens[2], tokens[3], CallFunc())
|
||||||
|
|
||||||
|
|
||||||
|
class DiscardStack(CodeOp):
|
||||||
|
def execute(self, vm):
|
||||||
|
vm.pop(1)
|
||||||
|
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
@tokenparser
|
||||||
|
def parse(tokens):
|
||||||
|
return codejoin(DiscardStack())
|
||||||
|
|
||||||
|
class ExtractStack(CodeOp):
|
||||||
|
def __init__(self, callback, count):
|
||||||
|
CodeOp.__init__(count)
|
||||||
|
self.callback = callback
|
||||||
|
|
||||||
|
def execute(self, vm):
|
||||||
|
count, = vm.pop(1)
|
||||||
|
self.callback(vm.pop(count))
|
||||||
|
|
||||||
|
class KeywordReturn(CodeOp):
|
||||||
|
def execute(self, vm):
|
||||||
|
vm.jump_code(EndContext)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
@tokenparser
|
||||||
|
def parse(tokens):
|
||||||
|
return codejoin(tokens[1:], KeywordReturn())
|
||||||
|
|
||||||
|
class LoopBreak(CodeOp):
|
||||||
|
def execute(self, vm):
|
||||||
|
vm.jump("break")
|
||||||
|
|
||||||
|
class LoopContinue(CodeOp):
|
||||||
|
def execute(self, vm):
|
||||||
|
vm.jump("cont")
|
||||||
|
|
||||||
|
class StartContext(CodeOp):
|
||||||
|
def execute(self, vm):
|
||||||
|
vm.push_context()
|
||||||
|
|
||||||
|
class EndContext(CodeOp):
|
||||||
|
def execute(self, vm):
|
||||||
|
vm.pop_context()
|
||||||
|
|
||||||
|
class LabelCodeOp(CodeOp):
|
||||||
|
def __init__(self, label):
|
||||||
|
CodeOp.__init__(self)
|
||||||
|
self.label = label
|
||||||
|
def execute(self, vm):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def ticks(self):
|
||||||
|
return 0
|
||||||
|
|
||||||
|
class JumpUncond(LabelCodeOp):
|
||||||
|
def execute(self, vm):
|
||||||
|
vm.jump(self.label)
|
||||||
|
|
||||||
|
class JumpIfFalse(LabelCodeOp):
|
||||||
|
def execute(self, vm):
|
||||||
|
cond, = vm.pop(1)
|
||||||
|
if not cond:
|
||||||
|
vm.jump(self.label)
|
||||||
|
|
||||||
|
class JumpIfTrue(LabelCodeOp):
|
||||||
|
def execute(self, vm):
|
||||||
|
cond, = vm.pop(1)
|
||||||
|
if cond:
|
||||||
|
vm.jump(self.label)
|
||||||
|
|
||||||
|
class EndBlock(LabelCodeOp):
|
||||||
|
def execute(self, vm):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class FinallyBlock(EndBlock):
|
||||||
|
def execute(self, vm):
|
||||||
|
if vm.exc_stack:
|
||||||
|
# finally can be invoked by the exception handler itself, so
|
||||||
|
# it's possible the error has not been handled yet.
|
||||||
|
# if there is an unhandled exception still on the stack, then
|
||||||
|
# continue searching for a handler.
|
||||||
|
vm.reraise()
|
||||||
|
|
||||||
|
|
||||||
|
class ExcHandlerBlock(EndBlock):
|
||||||
|
def __init__(self, label, code):
|
||||||
|
EndBlock.__init__(self, label)
|
||||||
|
|
||||||
|
self.code = code
|
||||||
|
|
||||||
|
def execute(self, vm):
|
||||||
|
while vm.exc_stack:
|
||||||
|
# get the exception that's being handled and place it on the real stack
|
||||||
|
exc = vm.exc_pop()
|
||||||
|
vm.push(exc)
|
||||||
|
|
||||||
|
# and queue up the exception handler code
|
||||||
|
vm.code_push(self.code)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class WhileBlock(CodeOp):
|
||||||
|
def __init__(self):
|
||||||
|
CodeOp.__init__(self)
|
||||||
|
|
||||||
|
self.cond = None
|
||||||
|
self.block = []
|
||||||
|
|
||||||
|
def execute(self, vm):
|
||||||
|
# load the block's code into the vm at runtime
|
||||||
|
# prevents conflicts with jump targets
|
||||||
|
vm.code_push(codejoin(
|
||||||
|
self.cond,
|
||||||
|
JumpIfFalse("break"),
|
||||||
|
self.block,
|
||||||
|
EndBlock("cont"),
|
||||||
|
self,
|
||||||
|
EndBlock("break")
|
||||||
|
))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
@tokenparser
|
||||||
|
def parse(tokens):
|
||||||
|
rv = WhileBlock()
|
||||||
|
|
||||||
|
for i in xrange(1, len(tokens)):
|
||||||
|
tok = tokens[i]
|
||||||
|
if rv.cond == None:
|
||||||
|
rv.cond = tok
|
||||||
|
else:
|
||||||
|
rv.block.append(tok)
|
||||||
|
|
||||||
|
return rv
|
||||||
|
|
||||||
|
class TryBlock(CodeOp):
|
||||||
|
def __init__(self):
|
||||||
|
CodeOp.__init__(self)
|
||||||
|
|
||||||
|
self.var = None
|
||||||
|
self.blocks = {}
|
||||||
|
self.blocks = {"try": [], "except": [], "else": [], "finally": []}
|
||||||
|
|
||||||
|
def execute(self, vm):
|
||||||
|
code = []
|
||||||
|
code.append(self.blocks["try"])
|
||||||
|
|
||||||
|
if "else" in self.blocks:
|
||||||
|
code.append(JumpUncond("exc_else"))
|
||||||
|
elif "finally" in self.blocks:
|
||||||
|
code.append(JumpUncond("exc_finally"))
|
||||||
|
else:
|
||||||
|
code.append(JumpUncond("exc_normalexit"))
|
||||||
|
|
||||||
|
if "except" in self.blocks:
|
||||||
|
handler_code = codejoin(
|
||||||
|
SetVariable(self.var),
|
||||||
|
self.blocks["except"]
|
||||||
|
)
|
||||||
|
code.append(ExcHandlerBlock("exc_handler", handler_code))
|
||||||
|
if "finally" in self.blocks:
|
||||||
|
code.append(JumpUncond("exc_finally"))
|
||||||
|
else:
|
||||||
|
code.append(JumpUncond("exc_normalexit"))
|
||||||
|
|
||||||
|
if "else" in self.blocks:
|
||||||
|
vm.code_push(self.blocks["else"])
|
||||||
|
if "finally" in self.blocks:
|
||||||
|
code.append(JumpUncond("exc_finally"))
|
||||||
|
else:
|
||||||
|
code.append(JumpUncond("exc_normalexit"))
|
||||||
|
|
||||||
|
if "finally" in self.blocks:
|
||||||
|
code.append(FinallyBlock("exc_finally"))
|
||||||
|
code.append(self.blocks["finally"])
|
||||||
|
|
||||||
|
code.append(EndBlock("exc_normalexit"))
|
||||||
|
vm.code_push(code)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
@tokenparser
|
||||||
|
def parse(tokens):
|
||||||
|
rv = TryBlock()
|
||||||
|
|
||||||
|
active_tok = None
|
||||||
|
for i in xrange(1, len(tokens)):
|
||||||
|
tok = tokens[i]
|
||||||
|
if tok in ("try", "except", "else", "finally"):
|
||||||
|
active_tok = tok
|
||||||
|
continue
|
||||||
|
|
||||||
|
if active_tok == "except" and self.var == None:
|
||||||
|
self.var = tok
|
||||||
|
else:
|
||||||
|
self.blocks[active_tok].append(tok)
|
||||||
|
|
||||||
|
return rv
|
||||||
|
|
||||||
|
class ForeachExtractList(CodeOp):
|
||||||
|
def __init__(self, foreach):
|
||||||
|
CodeOp.__init__(self)
|
||||||
|
self.foreach = foreach
|
||||||
|
self.pos = foreach.pos
|
||||||
|
|
||||||
|
def execute(self, vm):
|
||||||
|
ldata, = vm.pop(1)
|
||||||
|
if isinstance(ldata, dict):
|
||||||
|
vm.push(ldata.keys())
|
||||||
|
vm.push(0)
|
||||||
|
elif isinstance(ldata, list):
|
||||||
|
vm.push(ldata)
|
||||||
|
vm.push(0)
|
||||||
|
else:
|
||||||
|
vm.raise_exc("Error")
|
||||||
|
|
||||||
|
class ForeachPop(CodeOp):
|
||||||
|
def __init__(self, foreach):
|
||||||
|
CodeOp.__init__(self)
|
||||||
|
self.foreach = foreach
|
||||||
|
self.pos = foreach.pos
|
||||||
|
|
||||||
|
def execute(self, vm):
|
||||||
|
ldata, idx = vm.pop(2)
|
||||||
|
if idx > len(ldata):
|
||||||
|
"no more values to pop"
|
||||||
|
vm.push(0)
|
||||||
|
else:
|
||||||
|
"more values to pop"
|
||||||
|
val = ldata[idx]
|
||||||
|
vm.set_var(self.foreach.var, val)
|
||||||
|
vm.push(ldata)
|
||||||
|
vm.push(idx+1)
|
||||||
|
vm.push(1)
|
||||||
|
|
||||||
|
|
||||||
|
class ForeachIterator(CodeOp):
|
||||||
|
def __init__(self, foreach):
|
||||||
|
CodeOp.__init__(self)
|
||||||
|
self.foreach = foreach
|
||||||
|
self.pos = foreach.pos
|
||||||
|
|
||||||
|
def execute(self, vm):
|
||||||
|
vm.code_push(codejoin(
|
||||||
|
ForeachPop(),
|
||||||
|
JumpIfFalse("break"),
|
||||||
|
self.foreach.block,
|
||||||
|
EndBlock("cont"),
|
||||||
|
self,
|
||||||
|
EndBlock("break")
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
|
class ForeachBlock(CodeOp):
|
||||||
|
def __init__(self):
|
||||||
|
CodeOp.__init__(self)
|
||||||
|
|
||||||
|
self.src = None
|
||||||
|
self.list = None
|
||||||
|
self.var = None
|
||||||
|
self.block = []
|
||||||
|
|
||||||
|
|
||||||
|
def execute(self, vm):
|
||||||
|
# load the block's code into the vm at runtime
|
||||||
|
# prevents conflicts with jump targets
|
||||||
|
vm.code_push(codejoin(
|
||||||
|
self.src,
|
||||||
|
ForeachExtractList(),
|
||||||
|
self.new_iterator()
|
||||||
|
))
|
||||||
|
|
||||||
|
def new_iterator(self):
|
||||||
|
return ForeachIterator(self)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
@tokenparser
|
||||||
|
def parse(tokens):
|
||||||
|
rv = ForeachBlock()
|
||||||
|
|
||||||
|
for i in xrange(1, len(tokens)):
|
||||||
|
tok = tokens[i]
|
||||||
|
if rv.var == None:
|
||||||
|
rv.var = tok
|
||||||
|
elif tok == "in":
|
||||||
|
continue
|
||||||
|
elif rv.src == None:
|
||||||
|
rv.src = tok
|
||||||
|
else:
|
||||||
|
rv.block.append(tok)
|
||||||
|
|
||||||
|
return rv
|
||||||
|
|
||||||
|
|
||||||
|
class IfBlock(CodeOp):
|
||||||
|
def __init__(self):
|
||||||
|
CodeOp.__init__(self)
|
||||||
|
|
||||||
|
self.conditions = []
|
||||||
|
|
||||||
|
def execute(self, vm):
|
||||||
|
# load the ifblock code into the vm at runtime
|
||||||
|
# why, I don't know, but it makes my life easier
|
||||||
|
code = []
|
||||||
|
for cond, block in self.conditions:
|
||||||
|
code.append(cond)
|
||||||
|
code.append(JumpIfFalse("next"))
|
||||||
|
code.append(block)
|
||||||
|
code.append(JumpUncond("endif"))
|
||||||
|
code.append(EndBlock("next"))
|
||||||
|
code.append(EndBlock("endif"))
|
||||||
|
codeseq = codejoin(code)
|
||||||
|
vm.code_push(codeseq)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
@tokenparser
|
||||||
|
def parse(tokens):
|
||||||
|
conds = []
|
||||||
|
tok_count = 0
|
||||||
|
active_tok = None
|
||||||
|
for i in xrange(len(tokens)):
|
||||||
|
tok = tokens[i]
|
||||||
|
|
||||||
|
if tok == "endif":
|
||||||
|
break
|
||||||
|
if tok in ("if", "elseif", "else"):
|
||||||
|
tok_count += 1
|
||||||
|
active_tok = tok
|
||||||
|
else:
|
||||||
|
if tok_count > len(conds):
|
||||||
|
if active_tok == "else":
|
||||||
|
cond = [StackLiteral(1)]
|
||||||
|
block = tok
|
||||||
|
else:
|
||||||
|
cond = [tok]
|
||||||
|
block = []
|
||||||
|
conds.append([cond, [block]])
|
||||||
|
else:
|
||||||
|
conds[tok_count-1][1].append(tok)
|
||||||
|
|
||||||
|
ib = IfBlock()
|
||||||
|
conds = [[optimizer.optimize(x), optimizer.optimize(y)] for x, y in conds]
|
||||||
|
ib.conditions = conds
|
||||||
|
return ib
|
10
database.py
10
database.py
|
@ -1,11 +1,5 @@
|
||||||
from parse import static_parser
|
from parse import static_parser
|
||||||
|
from language import ObjRef
|
||||||
class ObjRef(object):
|
|
||||||
def __init__(self, objnum):
|
|
||||||
self.objnum = objnum
|
|
||||||
|
|
||||||
def __eq__(self, other):
|
|
||||||
return self.objnum == other.objnum
|
|
||||||
|
|
||||||
|
|
||||||
class DBObject(object):
|
class DBObject(object):
|
||||||
|
@ -88,7 +82,7 @@ class Database(object):
|
||||||
so = DBObject(ObjRef(0))
|
so = DBObject(ObjRef(0))
|
||||||
self.objects = [so]
|
self.objects = [so]
|
||||||
|
|
||||||
so.
|
|
||||||
|
|
||||||
def set_property(self, obj, prop, val):
|
def set_property(self, obj, prop, val):
|
||||||
o = self.get(obj)
|
o = self.get(obj)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
|
# Dead Simple Database
|
||||||
|
|
||||||
class DSDB_Database(Database):
|
class DSDB_Database(Database):
|
||||||
def __init__(self, dbname):
|
def __init__(self, dbname):
|
||||||
|
|
981
language.py
981
language.py
|
@ -1,989 +1,12 @@
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
from builtins import builtin_map
|
|
||||||
from database import ObjRef
|
|
||||||
import bisect
|
import bisect
|
||||||
import optimizer
|
import optimizer
|
||||||
import traceback
|
import traceback
|
||||||
from pyparsing import ParseException
|
from pyparsing import ParseException
|
||||||
|
from language_types import *
|
||||||
|
from bytecode import *
|
||||||
|
|
||||||
def lineno(s, loc):
|
|
||||||
if hash(s) in lineno.cache:
|
|
||||||
cache = lineno.cache[hash(s)]
|
|
||||||
else:
|
|
||||||
cache = []
|
|
||||||
|
|
||||||
i = 0
|
|
||||||
while True:
|
|
||||||
n = s[i:].find("\n") + i
|
|
||||||
if n < i:
|
|
||||||
break
|
|
||||||
cache.append(n)
|
|
||||||
i = n + 1
|
|
||||||
|
|
||||||
cache.append(len(s))
|
|
||||||
|
|
||||||
lineno.cache[hash(s)] = cache
|
|
||||||
|
|
||||||
cachepos = bisect.bisect_left(cache, loc)
|
|
||||||
line = cachepos + 1
|
|
||||||
if cachepos == 0:
|
|
||||||
char = loc + 1
|
|
||||||
else:
|
|
||||||
char = loc - cache[cachepos-1]
|
|
||||||
|
|
||||||
|
|
||||||
return (line, char)
|
|
||||||
|
|
||||||
lineno.cache = {}
|
|
||||||
|
|
||||||
def tokenparser(func):
|
|
||||||
def newfunc(s, loc, tokens):
|
|
||||||
try:
|
|
||||||
rv = func(tokens)
|
|
||||||
pos = lineno(s, loc)
|
|
||||||
if isinstance(rv, VMBaseObject):
|
|
||||||
rv.pos = pos
|
|
||||||
return rv
|
|
||||||
assert not False in [isinstance(x, (VMBaseObject, list)) for x in rv]
|
|
||||||
for x in rv:
|
|
||||||
if isinstance(x, VMBaseObject) and x.pos == None:
|
|
||||||
x.pos = pos
|
|
||||||
except:
|
|
||||||
e = sys.exc_info()
|
|
||||||
if e[0] == ParseException:
|
|
||||||
raise
|
|
||||||
gd = globals()
|
|
||||||
funcobj = None
|
|
||||||
for x in gd:
|
|
||||||
if hasattr(gd[x], "parse") and gd[x].parse == newfunc:
|
|
||||||
funcobj = x
|
|
||||||
print "Error with %s.parse tokens: %s" % (funcobj, tokens)
|
|
||||||
traceback.print_exc(e)
|
|
||||||
raise
|
|
||||||
|
|
||||||
return [rv]
|
|
||||||
|
|
||||||
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:
|
|
||||||
if isinstance(arg, list):
|
|
||||||
rv.extend(arg)
|
|
||||||
else:
|
|
||||||
rv.append(arg)
|
|
||||||
return rv
|
|
||||||
#return flatten(rv, ltypes=(list,))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
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)
|
|
||||||
elif isinstance(value, VMType):
|
|
||||||
return value
|
|
||||||
else:
|
|
||||||
raise TypeError("Unknown type %s cannot be coerced to VMType" % (type(value),))
|
|
||||||
|
|
||||||
|
|
||||||
def uncoerce(value):
|
|
||||||
assert isinstance(value, VMType)
|
|
||||||
return value.value
|
|
||||||
|
|
||||||
|
|
||||||
class VMBaseObject(object):
|
|
||||||
def __init__(self):
|
|
||||||
self.pos = None
|
|
||||||
|
|
||||||
def bytecode(self):
|
|
||||||
return [self]
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "<%s>" % (self.__class__.__name__,)
|
|
||||||
|
|
||||||
class VMType(VMBaseObject):
|
|
||||||
def bytecode(self):
|
|
||||||
return [StackLiteral(self)]
|
|
||||||
|
|
||||||
class VMInteger(VMType):
|
|
||||||
def __init__(self, value):
|
|
||||||
VMType.__init__(self)
|
|
||||||
self.value = int(value)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
@tokenparser
|
|
||||||
def parse(tokens):
|
|
||||||
return StackLiteral(VMInteger(tokens[0]))
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "%s" % (self.value,)
|
|
||||||
|
|
||||||
class VMFloat(VMType):
|
|
||||||
def __init__(self, value):
|
|
||||||
VMType.__init__(self)
|
|
||||||
self.value = float(value)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
@tokenparser
|
|
||||||
def parse(tokens):
|
|
||||||
return StackLiteral(VMFloat(tokens[0]))
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "%s" % (self.value,)
|
|
||||||
|
|
||||||
|
|
||||||
class VMTable(VMType):
|
|
||||||
def __init__(self, value):
|
|
||||||
VMType.__init__(self)
|
|
||||||
self.value = dict(value)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
@tokenparser
|
|
||||||
def parse(tokens):
|
|
||||||
return StackLiteral(VMTable(tokens[0]))
|
|
||||||
|
|
||||||
class VMList(VMType):
|
|
||||||
def __init__(self, value):
|
|
||||||
VMType.__init__(self)
|
|
||||||
self.value = list(value)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
@tokenparser
|
|
||||||
def parse(tokens):
|
|
||||||
return StackLiteral(VMList())
|
|
||||||
|
|
||||||
class VMTablePair(VMType):
|
|
||||||
def __init__(self, value):
|
|
||||||
VMType.__init__(self)
|
|
||||||
self.key = key
|
|
||||||
self.value = value
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
@tokenparser
|
|
||||||
def parse(tokens):
|
|
||||||
return StackLiteral(VMList())
|
|
||||||
|
|
||||||
class VMString(VMType):
|
|
||||||
def __init__(self, value):
|
|
||||||
VMType.__init__(self)
|
|
||||||
if isinstance(value, unicode):
|
|
||||||
self.value = value
|
|
||||||
else:
|
|
||||||
self.value = unicode(str(value), 'ascii', 'ignore')
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "\"%s\"" % (repr(self.value)[1:].strip("'").replace("\\'", "'").replace('"', '\\"'),)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
@tokenparser
|
|
||||||
def parse(tokens):
|
|
||||||
return StackLiteral(VMString(tokens[0]))
|
|
||||||
|
|
||||||
class VMObjRef(VMType):
|
|
||||||
def __init__(self, value):
|
|
||||||
VMType.__init__(self)
|
|
||||||
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,)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
@tokenparser
|
|
||||||
def parse(tokens):
|
|
||||||
return StackLiteral(VMObjRef(int(tokens[1])))
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "#%s" % (self.value.objnum,)
|
|
||||||
|
|
||||||
class VMRef(VMBaseObject):
|
|
||||||
pass
|
|
||||||
|
|
||||||
class VMIdent(VMRef):
|
|
||||||
def __init__(self, name):
|
|
||||||
VMRef.__init__(self)
|
|
||||||
self.name = name
|
|
||||||
|
|
||||||
def bytecode(self):
|
|
||||||
return [StackLiteral(unicode(self.name))]
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
@tokenparser
|
|
||||||
def parse(tokens):
|
|
||||||
disallow_keywords(tokens)
|
|
||||||
return VMIdent(tokens[0])
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "<ident %s>" % (self.name,)
|
|
||||||
|
|
||||||
|
|
||||||
class VMVariable(VMRef):
|
|
||||||
def __init__(self, name):
|
|
||||||
VMRef.__init__(self)
|
|
||||||
self.name = name
|
|
||||||
|
|
||||||
def ref(self):
|
|
||||||
return [StackLiteral(unicode(self.name))]
|
|
||||||
|
|
||||||
def bytecode(self):
|
|
||||||
return codejoin(self.ref(), GetVariable())
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
@tokenparser
|
|
||||||
def parse(tokens):
|
|
||||||
disallow_keywords(tokens)
|
|
||||||
return VMVariable(tokens[0])
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "<variable %s>" % (self.name,)
|
|
||||||
|
|
||||||
class VMFileRef(VMRef):
|
|
||||||
def __init__(self, obj, name):
|
|
||||||
VMRef.__init__(self)
|
|
||||||
self.obj = obj
|
|
||||||
self.name = name
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
@tokenparser
|
|
||||||
def parse(tokens):
|
|
||||||
assert tokens[1] == "!"
|
|
||||||
return VMFileRef(tokens[0], tokens[2])
|
|
||||||
#return codejoin(tokens[0], StackLiteral(tokens[2]), GetProperty())
|
|
||||||
|
|
||||||
def ref(self):
|
|
||||||
return [self.obj, self.name]
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "<fileref %s!%s>" % (self.obj, self.name)
|
|
||||||
|
|
||||||
def bytecode(self):
|
|
||||||
return codejoin(self.ref(), GetFile())
|
|
||||||
|
|
||||||
class VMPropRef(VMRef):
|
|
||||||
def __init__(self, obj, prop):
|
|
||||||
VMRef.__init__(self)
|
|
||||||
self.obj = obj
|
|
||||||
self.prop = prop
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
@tokenparser
|
|
||||||
def parse(tokens):
|
|
||||||
assert tokens[1] == "."
|
|
||||||
return VMPropRef(tokens[0], tokens[2])
|
|
||||||
#return codejoin(tokens[0], StackLiteral(tokens[2]), GetProperty())
|
|
||||||
|
|
||||||
def ref(self):
|
|
||||||
return [self.obj, self.prop]
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "<propref %s.%s>" % (self.obj, self.prop)
|
|
||||||
|
|
||||||
def bytecode(self):
|
|
||||||
return codejoin(self.ref(), GetProperty())
|
|
||||||
|
|
||||||
class VMCoreRef(VMPropRef):
|
|
||||||
@staticmethod
|
|
||||||
@tokenparser
|
|
||||||
def parse(tokens):
|
|
||||||
return VMPropRef(VMObjRef(0), tokens[1])
|
|
||||||
|
|
||||||
class CodeOp(VMBaseObject):
|
|
||||||
def load_stack(self, vm):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def ticks(self):
|
|
||||||
return 1
|
|
||||||
|
|
||||||
class StackCodeOp(CodeOp):
|
|
||||||
def __init__(self, stack_value):
|
|
||||||
CodeOp.__init__(self)
|
|
||||||
self.stack_value = stack_value
|
|
||||||
|
|
||||||
def load_stack(self, vm):
|
|
||||||
vm.push(self.stack_value)
|
|
||||||
|
|
||||||
|
|
||||||
class NoOp(CodeOp):
|
|
||||||
def execute(self, vm):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def ticks(self):
|
|
||||||
return 0
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
@tokenparser
|
|
||||||
def parse(tokens):
|
|
||||||
return []
|
|
||||||
|
|
||||||
|
|
||||||
class GetProperty(CodeOp):
|
|
||||||
def execute(self, vm):
|
|
||||||
prop, obj = vm.pop(2)
|
|
||||||
vm.push(vm.db.get_property(obj, prop))
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
@tokenparser
|
|
||||||
def parse(tokens):
|
|
||||||
return codejoin(GetProperty())
|
|
||||||
|
|
||||||
class SetProperty(CodeOp):
|
|
||||||
def execute(self, vm):
|
|
||||||
val, prop, obj = vm.pop(3)
|
|
||||||
vm.db.set_property(obj, prop, val)
|
|
||||||
vm.push(val)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
@tokenparser
|
|
||||||
def parse(tokens):
|
|
||||||
return codejoin(StackLiteral(tokens[0]))
|
|
||||||
|
|
||||||
class GetFile(CodeOp):
|
|
||||||
def execute(self, vm):
|
|
||||||
prop, obj = vm.pop(2)
|
|
||||||
vm.push(vm.db.get_file(obj, prop))
|
|
||||||
|
|
||||||
class SetFile(CodeOp):
|
|
||||||
def execute(self, vm):
|
|
||||||
val, prop, obj = vm.pop(3)
|
|
||||||
vm.db.set_file(obj, prop, val)
|
|
||||||
vm.push(val)
|
|
||||||
|
|
||||||
class SetVariable(CodeOp):
|
|
||||||
def execute(self, vm):
|
|
||||||
val, varname = vm.pop(2)
|
|
||||||
vm.setvar(varname, val)
|
|
||||||
vm.push(val)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
@tokenparser
|
|
||||||
def parse(tokens):
|
|
||||||
assert isinstance(tokens[0], GetVariable)
|
|
||||||
return codejoin(SetVariable(tokens[0].stack_value))
|
|
||||||
|
|
||||||
|
|
||||||
class GetVariable(CodeOp):
|
|
||||||
def execute(self, vm):
|
|
||||||
varname, = vm.pop(1)
|
|
||||||
vm.push(vm.getvar(varname))
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
@tokenparser
|
|
||||||
def parse(tokens):
|
|
||||||
return codejoin(tokens[0], GetVariable())
|
|
||||||
|
|
||||||
class CallBuiltin(CodeOp):
|
|
||||||
def execute(self, 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(tokens[0], tokens[1], CallBuiltin())
|
|
||||||
|
|
||||||
class CallFunction(CodeOp):
|
|
||||||
def execute(self, vm):
|
|
||||||
obj, name = vm.pop(2)
|
|
||||||
vm.code_push(codejoin(StartContext(), vm.get_code(obj, name), EndContext()))
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
@tokenparser
|
|
||||||
def parse(tokens):
|
|
||||||
return codejoin(tokens[0], CallFunction())
|
|
||||||
|
|
||||||
class ArithAdd(CodeOp):
|
|
||||||
def execute(self, vm):
|
|
||||||
v1, v2 = vm.pop(2)
|
|
||||||
vm.push(v1+v2)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
@tokenparser
|
|
||||||
def parse(tokens):
|
|
||||||
if tokens[0] == "+":
|
|
||||||
return codejoin(tokens[1], ArithAdd())
|
|
||||||
else:
|
|
||||||
return codejoin(tokens[1], ArithSub())
|
|
||||||
|
|
||||||
class ArithSub(CodeOp):
|
|
||||||
def execute(self, vm):
|
|
||||||
v1, v2 = vm.pop(2)
|
|
||||||
vm.push(v1-v2)
|
|
||||||
|
|
||||||
|
|
||||||
class ArithMul(CodeOp):
|
|
||||||
def execute(self, vm):
|
|
||||||
v1, v2 = vm.pop(2)
|
|
||||||
vm.push(v1*v2)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
@tokenparser
|
|
||||||
def parse(tokens):
|
|
||||||
if tokens[0] == "*":
|
|
||||||
return codejoin(tokens[1], ArithMul())
|
|
||||||
else:
|
|
||||||
return codejoin(tokens[1], ArithDiv())
|
|
||||||
|
|
||||||
class ArithDiv(CodeOp):
|
|
||||||
def execute(self, vm):
|
|
||||||
v1, v2 = vm.pop(2)
|
|
||||||
vm.push(v1/v2)
|
|
||||||
|
|
||||||
class ArithExp(CodeOp):
|
|
||||||
def execute(self, vm):
|
|
||||||
v1, v2 = vm.pop(2)
|
|
||||||
vm.push(v1**v2)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
@tokenparser
|
|
||||||
def parse(tokens):
|
|
||||||
return codejoin(tokens[1], ArithExp())
|
|
||||||
|
|
||||||
|
|
||||||
class BoolCompare(CodeOp):
|
|
||||||
map = {
|
|
||||||
'==': lambda x, y: int(x==y),
|
|
||||||
'!=': lambda x, y: int(x!=y),
|
|
||||||
'>': lambda x, y: int(x>y),
|
|
||||||
'<': lambda x, y: int(x<y),
|
|
||||||
'<=': lambda x, y: int(x<=y),
|
|
||||||
'>=': lambda x, y: int(x>=y),
|
|
||||||
'in': lambda x, y: int(x in y)
|
|
||||||
}
|
|
||||||
|
|
||||||
def __init__(self, cmpfunc):
|
|
||||||
CodeOp.__init__(self)
|
|
||||||
self.cmp = cmpfunc
|
|
||||||
|
|
||||||
def execute(self, vm):
|
|
||||||
v1, v2 = vm.pop(2)
|
|
||||||
vm.push(self.cmp(v1, v2))
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
@tokenparser
|
|
||||||
def parse(tokens):
|
|
||||||
op = tokens[0]
|
|
||||||
|
|
||||||
return codejoin(tokens[1], BoolCompare(BoolCompare.map[op]))
|
|
||||||
|
|
||||||
|
|
||||||
class BoolLogic(CodeOp):
|
|
||||||
map = {
|
|
||||||
'&&': lambda x, y: int(x and y),
|
|
||||||
'||': lambda x, y: int(x or y),
|
|
||||||
'~~': lambda x, y: int((x or y) and not (x and y)),
|
|
||||||
'and': lambda x, y: int(x and y),
|
|
||||||
'or': lambda x, y: int(x or y),
|
|
||||||
'xor': lambda x, y: int((x or y) and not (x and y)),
|
|
||||||
}
|
|
||||||
|
|
||||||
def __init__(self, cmpfunc):
|
|
||||||
CodeOp.__init__(self)
|
|
||||||
self.cmp = cmpfunc
|
|
||||||
|
|
||||||
def execute(self, vm):
|
|
||||||
v1, v2 = vm.pop(2)
|
|
||||||
vm.push(self.cmp(v1, v2))
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
@tokenparser
|
|
||||||
def parse(tokens):
|
|
||||||
op = tokens[0]
|
|
||||||
|
|
||||||
return codejoin(tokens[1], BoolLogic(BoolLogic.map[op]))
|
|
||||||
|
|
||||||
class UnaryOp(CodeOp):
|
|
||||||
map = {
|
|
||||||
"!": lambda x: int(not x),
|
|
||||||
"-": lambda x: -x
|
|
||||||
}
|
|
||||||
|
|
||||||
def __init__(self, cmpfunc):
|
|
||||||
CodeOp.__init__(self)
|
|
||||||
self.cmp = cmpfunc
|
|
||||||
|
|
||||||
def execute(self, vm):
|
|
||||||
v = vm.pop(1)
|
|
||||||
vm.push(self.cmp(v))
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
@tokenparser
|
|
||||||
def parse(tokens):
|
|
||||||
rv = []
|
|
||||||
ops = []
|
|
||||||
for t in tokens:
|
|
||||||
if isinstance(t, (str, unicode)) and t in UnaryOp.map:
|
|
||||||
ops.append(UnaryOp(UnaryOp.map[t]))
|
|
||||||
else:
|
|
||||||
rv.append(t)
|
|
||||||
return codejoin(rv, ops)
|
|
||||||
|
|
||||||
class Assignment(CodeOp):
|
|
||||||
@staticmethod
|
|
||||||
@tokenparser
|
|
||||||
def parse(tokens):
|
|
||||||
if len(tokens) > 2:
|
|
||||||
assert tokens[1] == "="
|
|
||||||
var = tokens[0]
|
|
||||||
if isinstance(var, VMVariable):
|
|
||||||
var = var.ref() + [SetVariable()]
|
|
||||||
elif isinstance(var, VMPropRef):
|
|
||||||
var = var.ref() + [SetProperty()]
|
|
||||||
elif isinstance(var, VMFileRef):
|
|
||||||
var = var.ref() + [SetFile()]
|
|
||||||
else:
|
|
||||||
raise ValueError, "Assignment to unknown type: %s" % (var,)
|
|
||||||
|
|
||||||
return codejoin(tokens[2:], var)
|
|
||||||
return tokens
|
|
||||||
|
|
||||||
class StackLiteral(StackCodeOp):
|
|
||||||
def execute(self, vm):
|
|
||||||
vm.push(self.stack_value)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
@tokenparser
|
|
||||||
def parse(tokens):
|
|
||||||
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),)
|
|
||||||
|
|
||||||
|
|
||||||
class StackToList(CodeOp):
|
|
||||||
def execute(self, vm):
|
|
||||||
count, = vm.pop(1)
|
|
||||||
stacklist = vm.pop_raw(count)
|
|
||||||
i = 0
|
|
||||||
while i < len(stacklist):
|
|
||||||
if isinstance(stacklist[i], VMList) and stacklist[i].flat:
|
|
||||||
nexti = i + len(stacklist[i])
|
|
||||||
stacklist[i:i+1] = uncoerce(stacklist[i])
|
|
||||||
else:
|
|
||||||
stacklist[i] = uncoerce(stacklist[i])
|
|
||||||
nexti = i + 1
|
|
||||||
|
|
||||||
i = nexti
|
|
||||||
vm.push(stacklist)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
@tokenparser
|
|
||||||
def parse(tokens):
|
|
||||||
if not tokens:
|
|
||||||
return codejoin(StackLiteral(0), StackToList())
|
|
||||||
rv = codejoin(tokens[0][0], StackLiteral(len(tokens)), StackToList())
|
|
||||||
return rv
|
|
||||||
|
|
||||||
class Flatten(CodeOp):
|
|
||||||
def execute(self, vm):
|
|
||||||
flatlist, = vm.pop_raw(1)
|
|
||||||
flatlist.flat = 1
|
|
||||||
vm.push(flatlist)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
@tokenparser
|
|
||||||
def parse(tokens):
|
|
||||||
if len(tokens) > 0 and tokens[0] == '@':
|
|
||||||
return codejoin(tokens[0], Flatten())
|
|
||||||
else:
|
|
||||||
return tokens
|
|
||||||
|
|
||||||
|
|
||||||
class CallFunc(CodeOp):
|
|
||||||
def execute(self, vm):
|
|
||||||
obj, funcname, args = vm.pop(3)
|
|
||||||
vm.push_context(args)
|
|
||||||
vm.push_code(codejoin(
|
|
||||||
vm.get_code(obj, funcname),
|
|
||||||
EndContext()
|
|
||||||
))
|
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
@tokenparser
|
|
||||||
def parse(tokens):
|
|
||||||
assert len(tokens) == 4 and tokens[1] == ":"
|
|
||||||
return codejoin(tokens[0], tokens[2], tokens[3], CallFunc())
|
|
||||||
|
|
||||||
|
|
||||||
class DiscardStack(CodeOp):
|
|
||||||
def execute(self, vm):
|
|
||||||
vm.pop(1)
|
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
@tokenparser
|
|
||||||
def parse(tokens):
|
|
||||||
return codejoin(DiscardStack())
|
|
||||||
|
|
||||||
class ExtractStack(CodeOp):
|
|
||||||
def __init__(self, callback, count):
|
|
||||||
CodeOp.__init__(count)
|
|
||||||
self.callback = callback
|
|
||||||
|
|
||||||
def execute(self, vm):
|
|
||||||
count, = vm.pop(1)
|
|
||||||
self.callback(vm.pop(count))
|
|
||||||
|
|
||||||
class KeywordReturn(CodeOp):
|
|
||||||
def execute(self, vm):
|
|
||||||
vm.jump_code(EndContext)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
@tokenparser
|
|
||||||
def parse(tokens):
|
|
||||||
return codejoin(tokens[1:], KeywordReturn())
|
|
||||||
|
|
||||||
class LoopBreak(CodeOp):
|
|
||||||
def execute(self, vm):
|
|
||||||
vm.jump("break")
|
|
||||||
|
|
||||||
class LoopContinue(CodeOp):
|
|
||||||
def execute(self, vm):
|
|
||||||
vm.jump("cont")
|
|
||||||
|
|
||||||
class StartContext(CodeOp):
|
|
||||||
def execute(self, vm):
|
|
||||||
vm.push_context()
|
|
||||||
|
|
||||||
class EndContext(CodeOp):
|
|
||||||
def execute(self, vm):
|
|
||||||
vm.pop_context()
|
|
||||||
|
|
||||||
class LabelCodeOp(CodeOp):
|
|
||||||
def __init__(self, label):
|
|
||||||
CodeOp.__init__(self)
|
|
||||||
self.label = label
|
|
||||||
self.ticks = 0
|
|
||||||
def execute(self, vm):
|
|
||||||
pass
|
|
||||||
|
|
||||||
class JumpUncond(LabelCodeOp):
|
|
||||||
def execute(self, vm):
|
|
||||||
vm.jump(self.label)
|
|
||||||
|
|
||||||
class JumpIfFalse(LabelCodeOp):
|
|
||||||
def execute(self, vm):
|
|
||||||
cond, = vm.pop(1)
|
|
||||||
if not cond:
|
|
||||||
vm.jump(self.label)
|
|
||||||
|
|
||||||
class JumpIfTrue(LabelCodeOp):
|
|
||||||
def execute(self, vm):
|
|
||||||
cond, = vm.pop(1)
|
|
||||||
if cond:
|
|
||||||
vm.jump(self.label)
|
|
||||||
|
|
||||||
class EndBlock(LabelCodeOp):
|
|
||||||
def execute(self, vm):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class FinallyBlock(EndBlock):
|
|
||||||
def execute(self, vm):
|
|
||||||
if vm.exc_stack:
|
|
||||||
# finally can be invoked by the exception handler itself, so
|
|
||||||
# it's possible the error has not been handled yet.
|
|
||||||
# if there is an unhandled exception still on the stack, then
|
|
||||||
# continue searching for a handler.
|
|
||||||
vm.reraise()
|
|
||||||
|
|
||||||
|
|
||||||
class ExcHandlerBlock(EndBlock):
|
|
||||||
def __init__(self, label, code):
|
|
||||||
EndBlock.__init__(self, label)
|
|
||||||
|
|
||||||
self.code = code
|
|
||||||
|
|
||||||
def execute(self, vm):
|
|
||||||
while vm.exc_stack:
|
|
||||||
# get the exception that's being handled and place it on the real stack
|
|
||||||
exc = vm.exc_pop()
|
|
||||||
vm.push(exc)
|
|
||||||
|
|
||||||
# and queue up the exception handler code
|
|
||||||
vm.code_push(self.code)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class WhileBlock(CodeOp):
|
|
||||||
def __init__(self):
|
|
||||||
CodeOp.__init__(self)
|
|
||||||
|
|
||||||
self.cond = None
|
|
||||||
self.block = []
|
|
||||||
|
|
||||||
def execute(self, vm):
|
|
||||||
# load the block's code into the vm at runtime
|
|
||||||
# prevents conflicts with jump targets
|
|
||||||
vm.code_push(codejoin(
|
|
||||||
self.cond,
|
|
||||||
JumpIfFalse("break"),
|
|
||||||
self.block,
|
|
||||||
EndBlock("cont"),
|
|
||||||
self,
|
|
||||||
EndBlock("break")
|
|
||||||
))
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
@tokenparser
|
|
||||||
def parse(tokens):
|
|
||||||
rv = WhileBlock()
|
|
||||||
|
|
||||||
for i in xrange(1, len(tokens)):
|
|
||||||
tok = tokens[i]
|
|
||||||
if rv.cond == None:
|
|
||||||
rv.cond = tok
|
|
||||||
else:
|
|
||||||
rv.block.append(tok)
|
|
||||||
|
|
||||||
return rv
|
|
||||||
|
|
||||||
class TryBlock(CodeOp):
|
|
||||||
def __init__(self):
|
|
||||||
CodeOp.__init__(self)
|
|
||||||
|
|
||||||
self.var = None
|
|
||||||
self.blocks = {}
|
|
||||||
self.blocks = {"try": [], "except": [], "else": [], "finally": []}
|
|
||||||
|
|
||||||
def execute(self, vm):
|
|
||||||
code = []
|
|
||||||
code.append(self.blocks["try"])
|
|
||||||
|
|
||||||
if "else" in self.blocks:
|
|
||||||
code.append(JumpUncond("exc_else"))
|
|
||||||
elif "finally" in self.blocks:
|
|
||||||
code.append(JumpUncond("exc_finally"))
|
|
||||||
else:
|
|
||||||
code.append(JumpUncond("exc_normalexit"))
|
|
||||||
|
|
||||||
if "except" in self.blocks:
|
|
||||||
handler_code = codejoin(
|
|
||||||
SetVariable(self.var),
|
|
||||||
self.blocks["except"]
|
|
||||||
)
|
|
||||||
code.append(ExcHandlerBlock("exc_handler", handler_code))
|
|
||||||
if "finally" in self.blocks:
|
|
||||||
code.append(JumpUncond("exc_finally"))
|
|
||||||
else:
|
|
||||||
code.append(JumpUncond("exc_normalexit"))
|
|
||||||
|
|
||||||
if "else" in self.blocks:
|
|
||||||
vm.code_push(self.blocks["else"])
|
|
||||||
if "finally" in self.blocks:
|
|
||||||
code.append(JumpUncond("exc_finally"))
|
|
||||||
else:
|
|
||||||
code.append(JumpUncond("exc_normalexit"))
|
|
||||||
|
|
||||||
if "finally" in self.blocks:
|
|
||||||
code.append(FinallyBlock("exc_finally"))
|
|
||||||
code.append(self.blocks["finally"])
|
|
||||||
|
|
||||||
code.append(EndBlock("exc_normalexit"))
|
|
||||||
vm.code_push(code)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
@tokenparser
|
|
||||||
def parse(tokens):
|
|
||||||
rv = TryBlock()
|
|
||||||
|
|
||||||
active_tok = None
|
|
||||||
for i in xrange(1, len(tokens)):
|
|
||||||
tok = tokens[i]
|
|
||||||
if tok in ("try", "except", "else", "finally"):
|
|
||||||
active_tok = tok
|
|
||||||
continue
|
|
||||||
|
|
||||||
if active_tok == "except" and self.var == None:
|
|
||||||
self.var = tok
|
|
||||||
else:
|
|
||||||
self.blocks[active_tok].append(tok)
|
|
||||||
|
|
||||||
return rv
|
|
||||||
|
|
||||||
class ForeachExtractList(CodeOp):
|
|
||||||
def __init__(self, foreach):
|
|
||||||
CodeOp.__init__(self)
|
|
||||||
self.foreach = foreach
|
|
||||||
self.pos = foreach.pos
|
|
||||||
|
|
||||||
def execute(self, vm):
|
|
||||||
ldata, = vm.pop(1)
|
|
||||||
if isinstance(ldata, dict):
|
|
||||||
vm.push(ldata.keys())
|
|
||||||
vm.push(0)
|
|
||||||
elif isinstance(ldata, list):
|
|
||||||
vm.push(ldata)
|
|
||||||
vm.push(0)
|
|
||||||
else:
|
|
||||||
vm.raise_exc("Error")
|
|
||||||
|
|
||||||
class ForeachPop(CodeOp):
|
|
||||||
def __init__(self, foreach):
|
|
||||||
CodeOp.__init__(self)
|
|
||||||
self.foreach = foreach
|
|
||||||
self.pos = foreach.pos
|
|
||||||
|
|
||||||
def execute(self, vm):
|
|
||||||
ldata, idx = vm.pop(2)
|
|
||||||
if idx > len(ldata):
|
|
||||||
"no more values to pop"
|
|
||||||
vm.push(0)
|
|
||||||
else:
|
|
||||||
"more values to pop"
|
|
||||||
val = ldata[idx]
|
|
||||||
vm.set_var(self.foreach.var, val)
|
|
||||||
vm.push(ldata)
|
|
||||||
vm.push(idx+1)
|
|
||||||
vm.push(1)
|
|
||||||
|
|
||||||
|
|
||||||
class ForeachIterator(CodeOp):
|
|
||||||
def __init__(self, foreach):
|
|
||||||
CodeOp.__init__(self)
|
|
||||||
self.foreach = foreach
|
|
||||||
self.pos = foreach.pos
|
|
||||||
|
|
||||||
def execute(self, vm):
|
|
||||||
vm.code_push(codejoin(
|
|
||||||
ForeachPop(),
|
|
||||||
JumpIfFalse("break"),
|
|
||||||
self.foreach.block,
|
|
||||||
EndBlock("cont"),
|
|
||||||
self,
|
|
||||||
EndBlock("break")
|
|
||||||
))
|
|
||||||
|
|
||||||
|
|
||||||
class ForeachBlock(CodeOp):
|
|
||||||
def __init__(self):
|
|
||||||
CodeOp.__init__(self)
|
|
||||||
|
|
||||||
self.src = None
|
|
||||||
self.list = None
|
|
||||||
self.var = None
|
|
||||||
self.block = []
|
|
||||||
|
|
||||||
|
|
||||||
def execute(self, vm):
|
|
||||||
# load the block's code into the vm at runtime
|
|
||||||
# prevents conflicts with jump targets
|
|
||||||
vm.code_push(codejoin(
|
|
||||||
self.src,
|
|
||||||
ForeachExtractList(),
|
|
||||||
self.new_iterator()
|
|
||||||
))
|
|
||||||
|
|
||||||
def new_iterator(self):
|
|
||||||
return ForeachIterator(self)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
@tokenparser
|
|
||||||
def parse(tokens):
|
|
||||||
rv = ForeachBlock()
|
|
||||||
|
|
||||||
for i in xrange(1, len(tokens)):
|
|
||||||
tok = tokens[i]
|
|
||||||
if rv.var == None:
|
|
||||||
rv.var = tok
|
|
||||||
elif tok == "in":
|
|
||||||
continue
|
|
||||||
elif rv.src == None:
|
|
||||||
rv.src = tok
|
|
||||||
else:
|
|
||||||
rv.block.append(tok)
|
|
||||||
|
|
||||||
return rv
|
|
||||||
|
|
||||||
|
|
||||||
class IfBlock(CodeOp):
|
|
||||||
def __init__(self):
|
|
||||||
CodeOp.__init__(self)
|
|
||||||
|
|
||||||
self.conditions = []
|
|
||||||
|
|
||||||
def execute(self, vm):
|
|
||||||
# load the ifblock code into the vm at runtime
|
|
||||||
# why, I don't know, but it makes my life easier
|
|
||||||
code = []
|
|
||||||
for cond, block in self.conditions:
|
|
||||||
code.append(cond)
|
|
||||||
code.append(JumpIfFalse("next"))
|
|
||||||
code.append(block)
|
|
||||||
code.append(JumpUncond("endif"))
|
|
||||||
code.append(EndBlock("next"))
|
|
||||||
code.append(EndBlock("endif"))
|
|
||||||
vm.code_push(code)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
@tokenparser
|
|
||||||
def parse(tokens):
|
|
||||||
conds = []
|
|
||||||
tok_count = 0
|
|
||||||
active_tok = None
|
|
||||||
for i in xrange(len(tokens)):
|
|
||||||
tok = tokens[i]
|
|
||||||
|
|
||||||
if tok == "endif":
|
|
||||||
break
|
|
||||||
if tok in ("if", "elseif", "else"):
|
|
||||||
tok_count += 1
|
|
||||||
active_tok = tok
|
|
||||||
else:
|
|
||||||
if tok_count > len(conds):
|
|
||||||
if active_tok == "else":
|
|
||||||
cond = [StackLiteral(1)]
|
|
||||||
block = tok
|
|
||||||
else:
|
|
||||||
cond = [tok]
|
|
||||||
block = []
|
|
||||||
conds.append([cond, [block]])
|
|
||||||
else:
|
|
||||||
conds[tok_count-1][1].append(tok)
|
|
||||||
|
|
||||||
ib = IfBlock()
|
|
||||||
conds = [[optimizer.optimize(x), optimizer.optimize(y)] for x, y in conds]
|
|
||||||
ib.conditions = conds
|
|
||||||
return ib
|
|
||||||
|
|
|
@ -0,0 +1,222 @@
|
||||||
|
from language_tools import *
|
||||||
|
from pyparsing import ParseException
|
||||||
|
from bytecode import *
|
||||||
|
|
||||||
|
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(','))
|
||||||
|
|
||||||
|
|
||||||
|
class ObjRef(object):
|
||||||
|
def __init__(self, objnum):
|
||||||
|
self.objnum = objnum
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
return self.objnum == other.objnum
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class VMType(VMBaseObject):
|
||||||
|
def bytecode(self):
|
||||||
|
return [StackLiteral(self)]
|
||||||
|
|
||||||
|
class VMInteger(VMType):
|
||||||
|
def __init__(self, value):
|
||||||
|
VMType.__init__(self)
|
||||||
|
self.value = int(value)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
@tokenparser
|
||||||
|
def parse(tokens):
|
||||||
|
return StackLiteral(VMInteger(tokens[0]))
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "%s" % (self.value,)
|
||||||
|
|
||||||
|
class VMFloat(VMType):
|
||||||
|
def __init__(self, value):
|
||||||
|
VMType.__init__(self)
|
||||||
|
self.value = float(value)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
@tokenparser
|
||||||
|
def parse(tokens):
|
||||||
|
return StackLiteral(VMFloat(tokens[0]))
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "%s" % (self.value,)
|
||||||
|
|
||||||
|
|
||||||
|
class VMTable(VMType):
|
||||||
|
def __init__(self, value):
|
||||||
|
VMType.__init__(self)
|
||||||
|
self.value = dict(value)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
@tokenparser
|
||||||
|
def parse(tokens):
|
||||||
|
return StackLiteral(VMTable(tokens[0]))
|
||||||
|
|
||||||
|
class VMList(VMType):
|
||||||
|
def __init__(self, value):
|
||||||
|
VMType.__init__(self)
|
||||||
|
self.value = list(value)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
@tokenparser
|
||||||
|
def parse(tokens):
|
||||||
|
return StackLiteral(VMList())
|
||||||
|
|
||||||
|
class VMTablePair(VMType):
|
||||||
|
def __init__(self, value):
|
||||||
|
VMType.__init__(self)
|
||||||
|
self.key = key
|
||||||
|
self.value = value
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
@tokenparser
|
||||||
|
def parse(tokens):
|
||||||
|
return StackLiteral(VMList())
|
||||||
|
|
||||||
|
class VMString(VMType):
|
||||||
|
def __init__(self, value):
|
||||||
|
VMType.__init__(self)
|
||||||
|
if isinstance(value, unicode):
|
||||||
|
self.value = value
|
||||||
|
else:
|
||||||
|
self.value = unicode(str(value), 'ascii', 'ignore')
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "\"%s\"" % (repr(self.value)[1:].strip("'").replace("\\'", "'").replace('"', '\\"'),)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
@tokenparser
|
||||||
|
def parse(tokens):
|
||||||
|
return StackLiteral(VMString(tokens[0]))
|
||||||
|
|
||||||
|
class VMObjRef(VMType):
|
||||||
|
def __init__(self, value):
|
||||||
|
VMType.__init__(self)
|
||||||
|
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,)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
@tokenparser
|
||||||
|
def parse(tokens):
|
||||||
|
return StackLiteral(VMObjRef(int(tokens[1])))
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "#%s" % (self.value.objnum,)
|
||||||
|
|
||||||
|
class VMRef(VMBaseObject):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class VMIdent(VMRef):
|
||||||
|
def __init__(self, name):
|
||||||
|
VMRef.__init__(self)
|
||||||
|
self.name = name
|
||||||
|
|
||||||
|
def bytecode(self):
|
||||||
|
return [StackLiteral(unicode(self.name))]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
@tokenparser
|
||||||
|
def parse(tokens):
|
||||||
|
disallow_keywords(tokens)
|
||||||
|
return VMIdent(tokens[0])
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "<ident %s>" % (self.name,)
|
||||||
|
|
||||||
|
|
||||||
|
class VMVariable(VMRef):
|
||||||
|
def __init__(self, name):
|
||||||
|
VMRef.__init__(self)
|
||||||
|
self.name = name
|
||||||
|
|
||||||
|
def ref(self):
|
||||||
|
return [StackLiteral(unicode(self.name))]
|
||||||
|
|
||||||
|
def bytecode(self):
|
||||||
|
return codejoin(self.ref(), GetVariable())
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
@tokenparser
|
||||||
|
def parse(tokens):
|
||||||
|
disallow_keywords(tokens)
|
||||||
|
return VMVariable(tokens[0])
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "<variable %s>" % (self.name,)
|
||||||
|
|
||||||
|
class VMFileRef(VMRef):
|
||||||
|
def __init__(self, obj, name):
|
||||||
|
VMRef.__init__(self)
|
||||||
|
self.obj = obj
|
||||||
|
self.name = name
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
@tokenparser
|
||||||
|
def parse(tokens):
|
||||||
|
assert tokens[1] == "!"
|
||||||
|
return VMFileRef(tokens[0], tokens[2])
|
||||||
|
#return codejoin(tokens[0], StackLiteral(tokens[2]), GetProperty())
|
||||||
|
|
||||||
|
def ref(self):
|
||||||
|
return [self.obj, self.name]
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "<fileref %s!%s>" % (self.obj, self.name)
|
||||||
|
|
||||||
|
def bytecode(self):
|
||||||
|
return codejoin(self.ref(), GetFile())
|
||||||
|
|
||||||
|
class VMPropRef(VMRef):
|
||||||
|
def __init__(self, obj, prop):
|
||||||
|
VMRef.__init__(self)
|
||||||
|
self.obj = obj
|
||||||
|
self.prop = prop
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
@tokenparser
|
||||||
|
def parse(tokens):
|
||||||
|
assert tokens[1] == "."
|
||||||
|
return VMPropRef(tokens[0], tokens[2])
|
||||||
|
#return codejoin(tokens[0], StackLiteral(tokens[2]), GetProperty())
|
||||||
|
|
||||||
|
def ref(self):
|
||||||
|
return [self.obj, self.prop]
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "<propref %s.%s>" % (self.obj, self.prop)
|
||||||
|
|
||||||
|
def bytecode(self):
|
||||||
|
return codejoin(self.ref(), GetProperty())
|
||||||
|
|
||||||
|
class VMCoreRef(VMPropRef):
|
||||||
|
@staticmethod
|
||||||
|
@tokenparser
|
||||||
|
def parse(tokens):
|
||||||
|
return VMPropRef(VMObjRef(0), tokens[1])
|
||||||
|
|
4
parse.py
4
parse.py
|
@ -198,7 +198,9 @@ class Parser(object):
|
||||||
#print self.parse(u"if (1) #740.xyz + -hello.world; endif")
|
#print self.parse(u"if (1) #740.xyz + -hello.world; endif")
|
||||||
|
|
||||||
data = unicode(open("test.moo", "r").read(), 'utf-8')
|
data = unicode(open("test.moo", "r").read(), 'utf-8')
|
||||||
print self.parse(data)
|
rv = self.parse(data)
|
||||||
|
print rv
|
||||||
|
return rv
|
||||||
|
|
||||||
|
|
||||||
static_parser = Parser()
|
static_parser = Parser()
|
||||||
|
|
1
test.moo
1
test.moo
|
@ -6,3 +6,4 @@ vxx = "abc";
|
||||||
else
|
else
|
||||||
vvv = #-1; vvx = $nothing;
|
vvv = #-1; vvx = $nothing;
|
||||||
endif
|
endif
|
||||||
|
serverlog(vvv);
|
|
@ -59,8 +59,8 @@ class VirtualMachine(object):
|
||||||
return self.task.code_stack.pop()
|
return self.task.code_stack.pop()
|
||||||
|
|
||||||
def code_push(self, code):
|
def code_push(self, code):
|
||||||
if isinstance(code, CodeOpSequence):
|
if isinstance(code, list):
|
||||||
for op in reverse(code):
|
for op in reversed(code):
|
||||||
self.task.code_stack.append(op)
|
self.task.code_stack.append(op)
|
||||||
else:
|
else:
|
||||||
self.task.code_stack.append(code)
|
self.task.code_stack.append(code)
|
||||||
|
@ -111,6 +111,11 @@ class VirtualMachine(object):
|
||||||
now = time.time()
|
now = time.time()
|
||||||
newtask = heapq.heappop(self.active_task_heap)
|
newtask = heapq.heappop(self.active_task_heap)
|
||||||
return self.activate_task(newtask)
|
return self.activate_task(newtask)
|
||||||
|
elif self.next_task_heap:
|
||||||
|
now = time.time()
|
||||||
|
newtask = heapq.heappop(self.next_task_heap)
|
||||||
|
return self.activate_task(newtask)
|
||||||
|
|
||||||
|
|
||||||
def activate_task(self, task):
|
def activate_task(self, task):
|
||||||
now = time.time()
|
now = time.time()
|
||||||
|
@ -153,7 +158,7 @@ class VirtualMachine(object):
|
||||||
|
|
||||||
|
|
||||||
def jump(self, target):
|
def jump(self, target):
|
||||||
while self.code_stack:
|
while self.task.code_stack:
|
||||||
nextop = self.code_pop()
|
nextop = self.code_pop()
|
||||||
if isinstance(nextop, EndBlock):
|
if isinstance(nextop, EndBlock):
|
||||||
if nextop.label == target:
|
if nextop.label == target:
|
||||||
|
@ -196,3 +201,14 @@ class VirtualMachine(object):
|
||||||
print "executing %s with stack %s" % (op, self.task.stack)
|
print "executing %s with stack %s" % (op, self.task.stack)
|
||||||
#op.load_stack(self)
|
#op.load_stack(self)
|
||||||
op.execute(self)
|
op.execute(self)
|
||||||
|
|
||||||
|
def test(self):
|
||||||
|
import parse
|
||||||
|
testcode = parse.static_parser.test()
|
||||||
|
self.spawn_forked_task(testcode, {})
|
||||||
|
self.run()
|
||||||
|
|
||||||
|
static_vm = VirtualMachine(None)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
static_vm.test()
|
Loading…
Add table
Reference in a new issue