超級簡單的虛擬機(Python 實現(xiàn))
我們這次實現(xiàn)的簡單虛擬機贯溅,和計算機的 cpu 有點類似。無非就是取指令,執(zhí)行指令之類的操作碱璃。
常見的虛擬機通常分為兩類褐荷,一種是棧式虛擬機勾效,另一種是寄存器虛擬機。比如說 CPython, Jvm 就是基于棧的虛擬機叛甫,而 lua 則是基于寄存器的虛擬機层宫。
我們這次實現(xiàn)的“玩具”虛擬機,就是一種基于棧的虛擬機其监。
虛擬機有三個重要屬性萌腿,code 代表要執(zhí)行的指令列表,stack 用于保存臨時變量抖苦,而 addr 代表當(dāng)前指令的地址毁菱。
# Python高效編程
class Machine:
def __init__(self, code):
self.code = code
self.stack = list()
self.addr = 0
原理其實很簡單,我們通過不斷獲取當(dāng)前指令地址锌历,從指令列表中獲取指令和數(shù)據(jù)贮庞,如果是數(shù)字或者字符串,就壓入棧中究西;如果是指令窗慎,就執(zhí)行相應(yīng)函數(shù)。
為了少些幾個字符卤材,我們向 Machine 類中添加幾個方法:
def push(self, value):
self.stack.append(value)
def pop(self):
return self.stack.pop()
@property
def top(self):
return self.stack[-1]
我們通過 dispatch 方法遮斥,來判斷當(dāng)前從指令列表中取得的片段是指令還是數(shù)據(jù):
def dispatch(self, opcode):
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,
"exit": self.exit,
"if": self.if_stmt,
"jmp": self.jmp,
"over": self.over,
"print": self.print,
"println": self.println,
"read": self.read,
"stack": self.dump_stack,
"swap": self.swap,
}
if opcode in dispatch_map:
dispatch_map[opcode]()
elif isinstance(opcode, int):
self.push(opcode)
elif isinstance(opcode, str)\
and opcode[0] == opcode[-1] == '"':
self.push(opcode[1:-1])
dispatch_map 就對應(yīng)我們在 Machine 類中實現(xiàn)的方法。
比如說 plus 方法和 jmp 方法:
def plus(self):
v2 = self.pop()
v1 = self.pop()
self.push(v1 + v2)
def jmp(self):
addr = self.pop()
if 0 <= addr < len(self.code):
self.addr = addr
else:
raise RuntimeError("addr must be integer")
其余方法也很簡單扇丛,大家可以直接查看源代碼术吗。
好了,在加入一個 run 函數(shù)帆精,我們就可以解釋代碼了较屿。只要當(dāng)前地址小于指令長度材蹬,就不斷取指令,執(zhí)行指令吝镣。
def run(self):
while self.addr < len(self.code):
opcode = self.code[self.addr]
self.addr += 1
self.dispatch(opcode)
我們創(chuàng)建 Machine 類堤器,并執(zhí)行 run 函數(shù):
>>> from vm import Machine
>>> Machine([521, 1314,"+", 6, "*","println"]).run()
11010
我們還可以給虛擬機加一個交互式界面:
def repl(prompt="VM>> "):
welcome()
while True:
try:
text = read(prompt)
code = list(tokenize(text))
code = constants_fold(code)
Machine(code).run()
except (RuntimeError, IndexError):
stdout.write("1表達式不合法\n")
except KeyboardInterrupt:
stdout.write("請使用exit退出程序\n")
在讀取用戶輸入字符串之后,對字符串處理:
def parse_word(word):
try:
return int(word)
except ValueError:
try:
return float(word)
except ValueError:
return word
def tokenize(text):
for word in text.split():
yield parse_word(word)
最后放張效果圖:
關(guān)注微信公眾號:Python高效編程末贾,在微信后臺回復(fù) 201991
獲取源代碼闸溃。