不,我还没有完成翻译问题。遗憾。
所以我为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语句中的某个地方,因为除了函数之外的所有内容都有效。
答案 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;p
将5
推送到堆栈上,然后p
将打印堆栈中的顶部元素。然后,你可以实现5;2;+
意味着push 5
,push 2
,让+
意味着add top two items on stack and push the result
......这样的添加......我将其留作练习; - )