用Python编写的解释器工作不正常

时间:2016-05-31 18:10:44

标签: python function dictionary interpreter

不,我还没有完成翻译问题。遗憾。

所以我为Python编写的编程语言编写了另一个解释器。编程语言有一个有趣的概念:你只获得一些基本的命令。要“获得”更复杂的命令,您必须编写函数并组合给您的简单命令。

无论如何,我为这门语言写了一个简单的翻译。

问题是:简单命令有效,但定义函数不起作用。

这是解释器(我删除了对解决问题无用的代码)。

class Interpreter:
    acc = 0
    defined = {}

    def read(self, cmd):
        tokens = cmd.replace("\n", ";").split(";")
        for token in tokens:
            self.parse(token)

    def parse(self, cmd):
        if cmd.startswith("def(") and cmd.endswith(")"):
            cmd = cmd[2:]
            cmd = cmd[:-1]
            cmd = cmd.split("|")
            self.defined[cmd[0]] = cmd[1]

        elif cmd in self.defined:
            self.read(self.defined[cmd])

        elif cmd == "1":
            self.acc += 1

        elif cmd == "2":
            print(self.acc)

        elif cmd == "i":
            self.acc = int(input(">> "))

i = Interpreter()
while 1:
    i.read(input("> "))

您可以使用语法def(name|code)定义函数。例如,def(cat|i;2)

现在,解决我遇到的问题。定义函数是不可能的。他们只是不工作。它不会抛出错误或任何东西。它什么都不做。

这是我尝试使用的代码:

def(c|i;2)
c

它应该输入并显示它,但它没有做任何事情。

但这有效:

i;2

在我看来问题是if cmd.startswith("def(") if语句中的某个地方,因为除了函数之外的所有内容都有效。

1 个答案:

答案 0 :(得分:2)

解决这些问题时,必须能够在程序运行时查看正在发生的事情。你可以,例如使用调试器,或者你可以使用古老的调试打印方法(就像我在下面所做的那样)。

我使用p命令扩展了解释器,该命令打印acc,并使其接受任何整数,否则它是相同的。

您遇到的问题是在将输入存储到defined之前销毁输入。我通过仅使用\n拆分外部命令和;来拆分def内的命令来解决它。

import textwrap

class Interpreter:
    acc = 0
    defined = {}

    def read(self, cmd):
        cmd = textwrap.dedent(cmd).strip()
        lines = cmd.split("\n")
        for line in lines:
            print '==> parsing:', line
            self.parse(line)

    def parse(self, cmd):
        if cmd.startswith("def(") and cmd.endswith(")"):
            print '::found def',
            name, code = cmd[4:-1].split('|')  # def( is 4 characters..
            self.defined[name] = code.replace(';', '\n')  # read() expects commands divided by \n, so replace ; before storing in self.defined
            print self.defined

        elif cmd in self.defined:
            print '::found defined name', cmd, '=>', `self.defined[cmd]`
            self.read(self.defined[cmd])

        elif cmd == "i":
            self.acc = int(input(">> "))

        elif cmd == "p":
            print(self.acc)

        else:
            self.acc += int(cmd)


intp = Interpreter()
intp.read("""
    def(c|i;2)
    c
    p
""")

运行的输出:

(dev) go|c:\srv\tmp> python pars.py
==> parsing: def(c|i;2)
::found def {'c': 'i\n2'}
==> parsing: c
::found defined name c => 'i\n2'
==> parsing: i
>> 5
==> parsing: 2
==> parsing: p
7
编写以这种方式递归调用自身的解释器有一些主要限制,因为编译语言中的每个函数调用都需要使用宿主语言(Python)进行函数调用。更好的方法是将程序转换为一堆命令,然后从堆栈中弹出一个命令并执行它。当堆栈为空时,你就完成了。然后,函数调用只涉及将定义的符号的值推送到堆栈上。我已经扩展了您的翻译,以便在下面执行此操作。我已经添加了一个命令x0,如果acc为零,它将退出函数调用(我在调用函数之前将$marker推入堆栈,因此我知道函数的位置呼叫开始):

def debug(*args):
    pass
    # print '[dbg]', ' '.join(str(a) for a in args)

class Interpreter:
    acc = 0
    defined = {}
    commands = []  # the stack

    def compile(self, program):
        program = textwrap.dedent(program).strip()
        lines = program.split("\n")
        lines.reverse()
        self.commands += lines

        while self.commands:
            command = self.commands.pop()
            debug('==> running:', command, 'stack:', self.commands)
            self.run_command(command)

    def run_command(self, cmd):
        if cmd.startswith("def(") and cmd.endswith(")"):
            name, code = cmd[4:-1].split('|')
            self.defined[name] = code.split(';')
            debug('::found def', self.defined)

        elif cmd in self.defined:
            debug('::found defined name', cmd, '=>', `self.defined[cmd]`)
            # mark command stack before executing function
            self.commands += ['$marker']  
            self.commands += list(reversed(self.defined[cmd]))

        elif cmd == '$marker':
            pass  # do nothing (we get here if a def doesn't have an x0 when the acc is zero)

        elif cmd == 'x0':
            # exit function call if acc is zero
            if self.acc == 0:
                while self.commands:  # pop the stack until we get to the $marker
                    tmp = self.commands.pop()
                    if tmp == '$marker':
                        break

        elif cmd == "i":
            self.acc = int(input(">> "))

        elif cmd == "p":
            print(self.acc)

        else:
            self.acc += int(cmd)

我们现在可以编写递归函数:

intp = Interpreter()
intp.compile("""
    4
    def(c|-1;x0;p;c)
    c
    p
""")

输出:

(dev) go|c:\srv\tmp> python pars.py  
3                                    
2                                    
1                                    
0                                    

而不是累加器(acc),将堆栈用于值也可能更具表现力,例如5;p5推送到堆栈上,然后p将打印堆栈中的顶部元素。然后,你可以实现5;2;+意味着push 5push 2,让+意味着add top two items on stack and push the result ......这样的添加......我将其留作练习; - )