Bei der Lösung dieser Art von Problemen ist es wichtig, in der Lage zu sehen, was passiert, während das Programm läuft. Sie können z.B. Verwenden Sie einen Debugger oder Sie können mit der uralten Debug-Druck-Methode gehen (wie ich unten getan habe).
Ich habe den Interpreter mit einem Befehl erweitert, der die acc
druckt, und es jede beliebige Ganzzahl akzeptiert, sonst ist es das gleiche.
Das Problem, das Sie haben, ist, dass Sie die Eingabe zerstören, bevor Sie es in defined
speichern können. Ich löse es, indem ich nur \n
verwende, um äußere Befehle zu teilen, und ;
, um Befehle innerhalb eines def
zu teilen.
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
""")
die Ausgabe von einem Lauf:
(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
einen Dolmetscher zu schreiben, die sich selbst rekursiv auf diese Weise ruft hat einige wesentliche Einschränkungen, da jeder Funktionsaufruf in der kompilierten Sprache erfordert in der Host-Sprache einen Funktionsaufruf (Python). Ein besserer Weg besteht darin, das Programm in einen Stapel von Befehlen umzuwandeln, dann einen Befehl vom Stapel zu holen und ihn auszuführen. Sie sind fertig, wenn der Stapel leer ist. Bei Funktionsaufrufen wird dann lediglich der Wert des definierten Symbols auf den Stapel geschoben. Ich habe Ihren Dolmetscher erweitert, um dies unten zu tun.Ich habe einen Befehl x0
hinzugefügt, die einen Funktionsaufruf beenden, wenn acc
Null ist (und ich schiebe ein $marker
auf den Stapel vor dem Aufruf einer Funktion, damit ich weiß, wo der Funktionsaufruf gestartet):
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)
wir können jetzt rekursive Funktionen schreiben:
intp = Interpreter()
intp.compile("""
4
def(c|-1;x0;p;c)
c
p
""")
die Ausgänge:
(dev) go|c:\srv\tmp> python pars.py
3
2
1
0
statt eines Akkumulators (acc
), ist es pr deutlich aussagekräftiger, um den Stapel auch für Werte zu verwenden, z. 5;p
würde 5
auf den Stapel schieben, dann würde p
das oberste Element auf dem Stapel drucken. Dann könnten Sie zusätzlich wie 5;2;+
implementieren push 5
bedeutet, push 2
und lassen +
add top two items on stack and push the result
meine ... Ich werde gehen, dass als excercise ;-)
Was ist Ihre Arbeits Abtastwerteingang? def (funktioniert überhaupt nicht – AK47
diese 'token = cmd.replace (" \ n ";"; "). split ("; ")' teilt das def in zwei "token": 'def (c | i' und '2)' – thebjorn