当前位置:   article > 正文

超级简单的虚拟机(Python 实现)

寄存器虚拟机

我们这次实现的简单虚拟机,和计算机的 cpu 有点类似。无非就是取指令,执行指令之类的操作。

常见的虚拟机通常分为两类,一种是栈式虚拟机,另一种是寄存器虚拟机。比如说 CPython, Jvm 就是基于栈的虚拟机,而 lua 则是基于寄存器的虚拟机。

我们这次实现的“玩具”虚拟机,就是一种基于栈的虚拟机。

虚拟机有三个重要属性,code 代表要执行的指令列表,stack 用于保存临时变量,而 addr 代表当前指令的地址。

  1. # Python高效编程
  2. class Machine:
  3.     def __init__(self, code):
  4.         self.code = code
  5.         self.stack = list()
  6.         self.addr = 0

原理其实很简单,我们通过不断获取当前指令地址,从指令列表中获取指令和数据,如果是数字或者字符串,就压入栈中;如果是指令,就执行相应函数。

为了少些几个字符,我们向 Machine 类中添加几个方法:

  1. def push(self, value):
  2.     self.stack.append(value)
  3. def pop(self):
  4.     return self.stack.pop()
  5. @property
  6. def top(self):
  7.     return self.stack[-1]

我们通过 dispatch 方法,来判断当前从指令列表中取得的片段是指令还是数据:

  1. def dispatch(self, opcode):
  2.     dispatch_map = {
  3.         "%":        self.mod,
  4.         "*":        self.mul,
  5.         "+":        self.plus,
  6.         "-":        self.minus,
  7.         "/":        self.div,
  8.         "==":       self.eq,
  9.         "cast_int": self.cast_int,
  10.         "cast_str": self.cast_str,
  11.         "drop":     self.drop,
  12.         "dup":      self.dup,
  13.         "exit":     self.exit,
  14.         "if":       self.if_stmt,
  15.         "jmp":      self.jmp,
  16.         "over":     self.over,
  17.         "print":    self.print,
  18.         "println":  self.println,
  19.         "read":     self.read,
  20.         "stack":    self.dump_stack,
  21.         "swap":     self.swap,
  22.         }
  23.     if opcode in dispatch_map:
  24.         dispatch_map[opcode]()
  25.     elif isinstance(opcode, int):
  26.         self.push(opcode)
  27.     elif isinstance(opcode, str)\
  28.         and opcode[0] == opcode[-1] == '"':
  29.         self.push(opcode[1:-1])

dispatch_map 就对应我们在 Machine 类中实现的方法。

比如说 plus 方法和 jmp 方法:

  1. def plus(self):
  2.     v2 = self.pop()
  3.     v1 = self.pop()
  4.     self.push(v1 + v2)
  5. def jmp(self):
  6.     addr = self.pop()
  7.     if 0 <= addr < len(self.code):
  8.         self.addr = addr
  9.     else:
  10.         raise RuntimeError("addr must be integer")

其余方法也很简单,大家可以直接查看源代码。

好了,在加入一个 run 函数,我们就可以解释代码了。只要当前地址小于指令长度,就不断取指令,执行指令。

  1. def run(self):
  2.     while self.addr < len(self.code):
  3.         opcode = self.code[self.addr]
  4.         self.addr += 1
  5.         self.dispatch(opcode)

我们创建 Machine 类,并执行 run 函数,注意字符串要用引号括起来

  1. >>> from vm import Machine
  2. >>> Machine([5211314,"+"6"*","println"]).run()
  3. 11010

我们还可以给虚拟机加一个交互式界面:

  1. def repl(prompt="VM>> "):
  2.     welcome()
  3.     while True:
  4.         try:
  5.             text = read(prompt)
  6.             code = list(tokenize(text))
  7.             code = constants_fold(code)
  8.             Machine(code).run()
  9.         except (RuntimeError, IndexError):
  10.             stdout.write("1表达式不合法\n")
  11.         except KeyboardInterrupt:
  12.             stdout.write("请使用exit退出程序\n")

在读取用户输入字符串之后,对字符串处理:

  1. def parse_word(word):
  2.     try:
  3.         return int(word)
  4.     except ValueError:
  5.         try:
  6.             return float(word)
  7.         except ValueError:
  8.             return word
  9. def tokenize(text):
  10.     for word in text.split():
  11.         yield parse_word(word)

最后放张效果图:

2b434ff383d1510cf88c7c1ef97a090c.gif

关注微信公众号:Python高效编程,在微信后台回复 201991获取源代码。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/199224?site
推荐阅读
相关标签
  

闽ICP备14008679号