使用python编写虚拟机解释器

本文参考自: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()

程序的运行结果:
这里写图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值