本文参考自:http://www.ituring.com.cn/article/199660
虚拟机是用来解释字节码指令的,通过使用python构造一个虚拟机解释器可以学到很多关于程序语言的知识,下面是具体的代码:
#!/usr/bin/python3
# encoding: utf-8
from io import StringIO
from collections import deque
import tokenize
import sys
class Stack(deque):
push = deque.append
def top(self):
return self[-1]
class Machine:
def __init__(self, code):
self.data_stack = Stack()
self.return_addr_stack = Stack()
self.instruction_pointer = 0
self.code = code
def pop(self):
return self.data_stack.pop()
def push(self, value):
self.data_stack.push(value)
def top(self):
return self.data_stack.top()
def run(self):
while self.instruction_pointer < len(self.code):
opcode = self.code[self.instruction_pointer]
self.instruction_pointer += 1
self.dispatch(opcode)
def dispatch(self, op):
dispatch_map = {
"%": self.mod,
"*": self.mul,
"+": self.plus,
"-": self.minus,
"/": self.div,
"==": self.eq,
"cast_int": self.cast_int,
"cast_str": self.cast_str,
"drop": self.drop,
"dup": self.dup,
"if": self.if_stmt,
"jmp": self.jmp,
"over": self.over,
"print": self.print,
"println": self.println,
"read": self.read,
"dump": self.dump,
"stack": self.stack,
"swap": self.swap,
}
if op in dispatch_map:
dispatch_map[op]()
elif isinstance(op, int):
self.push(op)
elif isinstance(op, str) and op[0] == op[-1] == '"':
self.push(op[1:-1])
else:
raise RuntimeError("Unknown opcode: '%s'" % op)
def mod(self):
self.push(self.pop() % self.pop())
def mul(self):
self.push(self.pop() * self.pop())
def plus(self):
self.push(self.pop() + self.pop())
def minus(self):
self.push(self.pop() - self.pop())
def div(self):
self.push(self.pop() / self.pop())
def eq(self):
self.push(self.top() == self.top())
def cast_int(self):
self.push(int(self.pop()))
def cast_str(self):
self.push(str(self.pop()))
def drop(self):
self.pop()
def dup(self):
self.push(self.top())
def if_stmt(self):
false_clause = self.pop()
true_clause = self.pop()
test = self.pop()
self.push(true_clause if test else false_clause)
def jmp(self):
addr = self.pop()
if (isinstance(addr, int) and 0 <= addr < len(self.code)):
self.instruction_pointer = addr
else:
raise RuntimeError("JMP address must be a valid integer.")
def over(self):
b = self.pop()
a = self.pop()
self.push(a)
self.push(b)
self.push(a)
def print(self):
sys.stdout.write(str(self.pop()))
sys.stdout.flush()
def println(self):
sys.stdout.write("%s\n" % self.pop())
sys.stdout.flush()
def dump(self):
pass
def read(self):
self.push(input())
def stack(self):
pass
def swap(self):
a = self.pop()
b = self.pop()
self.push(a)
self.push(b)
def parse(text):
tokens = tokenize.generate_tokens(StringIO(text).readline)
for toknum, tokval, _, _, _ in tokens:
if toknum == tokenize.NUMBER:
yield int(tokval)
elif toknum in [tokenize.OP, tokenize.STRING, tokenize.NAME]:
yield tokval
elif toknum == tokenize.ENDMARKER:
break
else:
raise RuntimeError("Unknown token %s: '%s'" %
(tokenize.tok_name[toknum], tokval))
def constant_fold(code):
"""Constant-folds simple mathematical expressions like 2 3 + to 5."""
while True:
# Find two consecutive numbers and an arithmetic operator
for i, (a, b, op) in enumerate(zip(code, code[1:], code[2:])):
if isinstance(a, int) and isinstance(b, int) \
and op in {"+", "-", "*", "/"}:
m = Machine((a, b, op))
m.run()
code[i:i+3] = [m.top()]
print("Constant-folded %s%s%s to %s" % (a, op, b, m.top()))
break
else:
break
return code
def repl():
print('Hit CTRL-D or type "exit" to quit.')
while True:
try:
source = input("> ")
code = list(parse(source))
code = constant_fold(code)
Machine(code).run()
except (RuntimeError, IndexError) as e:
print("IndexError: %s" % e)
except KeyboardInterrupt:
print("\nKeyBoardInterrupt")
if __name__ == "__main__":
# Machine([
# '"Enter a number: "', "print", "read", "cast_int",
# '"Enter another number: "', "print", "read", "cast_int",
# "over", "over",
# '"Their sum is: "', "print", "+", "println",
# '"Their product is: "', "print", "*", "println"
# ]).run()
# Machine([
# '"Enter a number: "', "print", "read", "cast_int",
# '"The number "', "print", "dup", "print", '" is "', "print",
# 2, "%", "==", '"even."', '"odd."', "if", "println",
# 0, "jmp" # loop forever!
# ]).run()
repl()
程序的运行结果: