Last active
June 25, 2016 15:13
-
-
Save felko/8998194d45e26ef5fe8f097358113ada to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3.4 | |
# coding: utf-8 | |
from collections import ChainMap | |
class Bytecode: | |
def __init__(self, instructions): | |
self.instructions = instructions | |
def __str__(self): | |
s = '' | |
for instr in self.instructions: | |
s += str(instr) + '\n' | |
return s | |
class Instruction: | |
pass | |
class Load(Instruction): | |
def __init__(self, id): | |
self.id = id | |
def __str__(self): | |
return 'LOAD {}'.format(self.id) | |
class Store(Instruction): | |
def __init__(self, id): | |
self.id = id | |
def __str__(self): | |
return 'STORE {}'.format(self.id) | |
class Add(Instruction): | |
def __str__(self): | |
return 'ADD' | |
class Sub(Instruction): | |
def __str__(self): | |
return 'SUB' | |
class Mul(Instruction): | |
def __str__(self): | |
return 'MUL' | |
class Div(Instruction): | |
def __str__(self): | |
return 'DIV' | |
class Pow(Instruction): | |
def __str__(self): | |
return 'POW' | |
class Call(Instruction): | |
def __init__(self, n): | |
self.n = n | |
def __str__(self): | |
return 'CALL {}'.format(self.n) | |
class PushInt(Instruction): | |
def __init__(self, value): | |
self.value = value | |
def __str__(self): | |
return 'PUSH_INT {}'.format(self.value) | |
class PushScope(Instruction): | |
def __str__(self): | |
return 'PUSH_SCOPE' | |
class PopScope(Instruction): | |
def __str__(self): | |
return 'POP_SCOPE' | |
class Pop(Instruction): | |
def __str__(self): | |
return 'POP' | |
class Proc(Instruction): | |
def __init__(self, params, instructions): | |
self.params = params | |
self.instructions = instructions | |
def __str__(self): | |
s = 'PROC [\n' | |
Proc.str_depth += 1 | |
for instr in self.instructions: | |
s += Proc.str_depth * ' ' + str(instr) + '\n' | |
s += ']' | |
Proc.str_depth -= 1 | |
return s | |
def __repr__(self): | |
return '<proc>' | |
str_depth = 0 | |
class VM: | |
executers = {} | |
def __init__(self, bytecode): | |
self.bytecode = bytecode | |
self.stack = [] | |
self.env = ChainMap() | |
@classmethod | |
def register(cls, optype): | |
def _executer_wrapper(fn): | |
cls.executers[optype] = fn | |
return fn | |
return _executer_wrapper | |
def execute(self, op): | |
self.executers[type(op)](self, op) | |
if isinstance(op, Proc): | |
print(str(op) + ' ' * 19 + '{:<70}{:<70}'.format(str(self.stack), str(self.env.maps))) | |
else: | |
print('{:<20}{:<70}{:<70}'.format(str(op), str(self.stack), str(self.env.maps))) | |
def run(self): | |
for op in self.bytecode.instructions: | |
self.execute(op) | |
@VM.register(Load) | |
def execute_load(vm, load): | |
obj = vm.env[load.id] | |
vm.stack.append(obj) | |
@VM.register(Store) | |
def execute_store(vm, store): | |
obj = vm.stack[-1] | |
vm.env.maps[0][store.id] = obj | |
@VM.register(Add) | |
def execute_add(vm, add): | |
x, y = vm.stack[-2:] | |
vm.stack.append(x + y) | |
@VM.register(Sub) | |
def execute_sub(vm, sub): | |
x, y = vm.stack[-2:] | |
vm.stack.append(x - y) | |
@VM.register(Mul) | |
def execute_mul(vm, mul): | |
x, y = vm.stack[-2:] | |
vm.stack.append(x * y) | |
@VM.register(Div) | |
def execute_div(vm, div): | |
x, y = vm.stack[-2:] | |
vm.stack.append(x // y) | |
@VM.register(Pow) | |
def execute_pow(vm, pow): | |
x, y = vm.stack[-2:] | |
vm.stack.append(x ** y) | |
@VM.register(Call) | |
def execute_add(vm, call): | |
stack_tmp = vm.stack.copy() | |
*args, proc = vm.stack[-call.n - 1:] | |
vm.env.maps.insert(0, dict(zip(proc.params, args))) | |
for instr in proc.instructions: | |
vm.execute(instr) | |
res = vm.stack[-1] | |
vm.env.maps.pop(0) | |
vm.stack = stack_tmp | |
vm.stack.append(res) | |
@VM.register(PushInt) | |
def execute_push_int(vm, push): | |
vm.stack.append(push.value) | |
@VM.register(PushScope) | |
def execute_push_scope(vm, push): | |
vm.env.maps.insert(0, {}) | |
@VM.register(PopScope) | |
def execute_pop_scope(vm, pop): | |
vm.env.maps.pop(0) | |
@VM.register(Pop) | |
def execute_push(vm, pop): | |
vm.stack.pop() | |
@VM.register(Proc) | |
def execute_proc(vm, proc): | |
vm.stack.append(proc) | |
if __name__ == '__main__': | |
bc = Bytecode([ | |
Proc(['x', 'y'], [ | |
Load('x'), | |
Load('y'), | |
Add() | |
]), | |
Store('add'), | |
Pop(), | |
PushInt(5), | |
PushInt(7), | |
Load('add'), | |
Call(2) | |
]) | |
vm = VM(bc) | |
vm.run() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment