1
Files
lecture-interpreters/de.churl.simple/bytecodeinterpreter.py
2021-09-01 22:49:06 +02:00

145 lines
5.9 KiB
Python

from disass import disassemble
import compile
from objspace import ObjectSpace
class ByteCodeError(Exception):
pass
class Interpreter(object):
def __init__(self, builtincode=None):
# Using an instance variable to keep the public interface
self.space = ObjectSpace(self)
self.space.setup_builtins(builtincode)
def eval(self, ast, w_context):
code = compile.compile(ast)
return self.run(code, w_context)
def read4(self, code, pc):
""" Converts 4 unicode characters to single 4 byte value """
highval = ord(code[pc + 3]) # most significant byte
if highval >= 128: # convert from 2's complement?
highval -= 256
return (ord(code[pc]) | # merge single bytes into 4 byte value
(ord(code[pc + 1]) << 8) |
(ord(code[pc + 2]) << 16) |
(highval << 24))
def run(self, bytecode, w_context):
pc = 0
stack = []
code = bytecode.code
# print(disassemble(bytecode))
while pc < len(code):
opcode = ord(code[pc]) # convert unicode to number
pc += 1
if compile.isjump(opcode):
oparg = self.read4(code, pc)
pc += 4
if opcode == compile.JUMP:
pc += oparg
elif opcode == compile.JUMP_IF_FALSE:
w_condition = stack.pop()
if self.space.isfalse(w_condition):
pc += oparg
elif compile.hasarg(opcode):
oparg = ord(code[pc])
pc += 1
if oparg >= 128:
if oparg > 128:
oparg -= 256
else:
oparg = self.read4(code, pc)
pc += 4
if opcode == compile.MAKE_OBJECT:
name = bytecode.symbols[oparg]
obj = self.space.newobject(name, {'__parent__': w_context}, [])
stack.append(obj)
elif opcode == compile.MAKE_OBJECT_CALL:
self.run(bytecode.subbytecodes[oparg], stack[-1])
# Project -----
elif opcode == compile.INT_LITERAL:
w_value = self.space.newint(oparg)
stack.append(w_value)
elif opcode == compile.BOOL_LITERAL:
w_value = self.space.newbool(oparg) # oparg is 1 or 0
stack.append(w_value)
elif opcode == compile.STRING_LITERAL:
value = bytecode.symbols[oparg]
w_value = self.space.newstring(value)
stack.append(w_value)
elif opcode == compile.DOUBLE_LITERAL:
value = bytecode.symbols[oparg]
w_value = self.space.newdouble(value)
stack.append(w_value)
# -------------
elif opcode == compile.MAKE_FUNCTION:
bc = bytecode.subbytecodes[oparg]
w_method = self.space.definemethod(name=bc.name, code=bc, w_target=w_context)
stack.append(w_method)
elif opcode == compile.METHOD_LOOKUP:
name = bytecode.symbols[oparg]
w_method = self.space.getvalue(stack[-1], name)
stack.append(w_method)
elif opcode == compile.METHOD_CALL:
arguments_w = [stack.pop() for _ in range(oparg)]
arguments_w.reverse()
#
w_method = stack.pop()
w_receiver = stack.pop()
w_result = self.space.call(w_method, w_receiver, arguments_w)
stack.append(w_result)
elif opcode == compile.PRIMITIVE_METHOD_CALL:
nargs = self.space.get_number_of_arguments_of_primitive(oparg)
arguments_w = [stack.pop() for _ in range(nargs)]
arguments_w.reverse()
w_receiver = stack.pop()
w_result = self.space.call_primitive(oparg, w_receiver, arguments_w)
stack.append(w_result)
elif opcode == compile.SET_LOCAL:
w_value = stack[-1]
name = bytecode.symbols[oparg]
self.space.setvalue(w_context, name, w_value)
elif opcode == compile.ASSIGNMENT:
w_value = stack.pop()
name = bytecode.symbols[oparg]
self.space.setvalue(stack[-1], name, w_value)
elif opcode == compile.ASSIGNMENT_APPEND_PARENT:
w_value = stack.pop()
name = bytecode.symbols[oparg]
self.space.setvalue(stack[-1], name, w_value)
self.space.addparent(stack[-1], name)
elif opcode == compile.GET_LOCAL:
name = bytecode.symbols[oparg]
w_value = self.space.getvalue(w_context, name)
w_value = self.space.call(w_value, w_context, [])
stack.append(w_value)
else:
raise ByteCodeError('Invalid bytecode with arguments')
else:
if opcode == compile.POP:
stack.pop()
elif opcode == compile.IMPLICIT_SELF:
stack.append(w_context)
elif opcode == compile.DUP:
stack.append(stack[-1])
# Project
elif opcode == compile.GC:
self.space.gc(w_context)
else:
raise ByteCodeError('Invalid bytecode')
assert pc == len(code)
assert len(stack) == 1
return stack.pop()
def make_module(self):
return self.space.make_module()