2016-05-31 11 views
0

Nein, ich bin noch nicht fertig mit meinen Interpreterfragen. Es tut uns leid.Interpreter geschrieben in Python funktioniert nicht richtig

Also habe ich einen anderen Interpreter für eine Programmiersprache geschrieben, die ich in Python erstellt habe. Die Programmiersprache hat ein interessantes Konzept: Sie erhalten nur einige grundlegende Befehle. Um komplexere Befehle zu "bekommen", müssen Sie Funktionen schreiben und den einfachen Befehl kombinieren, der Ihnen gegeben wurde.

Wie auch immer, ich habe einen einfachen Interpreter für die Sprache geschrieben.

Das Problem ist: die einfachen Befehle funktionieren, aber definieren Funktionen nicht.

Hier ist der Interpreter (Ich habe Code entfernt, der nicht zur Lösung des Problems nützlich ist).

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("> ")) 

Sie können Funktionen mit der Syntax def(name|code) definieren. Zum Beispiel def(cat|i;2).

Jetzt, auf das Problem, das ich habe. Es ist unmöglich, Funktionen zu definieren. Sie funktionieren einfach nicht. Es wirft keinen Fehler oder irgendetwas. Es tut nichts.

Hier ist der Code, den ich zu verwenden versucht:

def(c|i;2) 
c 

Es sollte Eingang bekommen und es angezeigt, aber stattdessen hat es nichts zu tun.

Dies funktioniert, aber:

i;2 

Es scheint mir, wie das Problem irgendwo in der if cmd.startswith("def(") if-Anweisung, weil alles außer der Funktionen arbeiten.

+0

Was ist Ihre Arbeits Abtastwerteingang? def (funktioniert überhaupt nicht – AK47

+0

diese 'token = cmd.replace (" \ n ";"; "). split ("; ")' teilt das def in zwei "token": 'def (c | i' und '2)' – thebjorn

Antwort

2

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 ;-)

+0

Danke für die Hilfe, Programm funktioniert jetzt perfekt :) Auch der Grund, warum ich einen Akku benutze, ist, weil die Programmiersprache eine knochige sein soll, auf die Sie leicht bauen können mit dem 'def () 'Befehl. Ich würde vielleicht in die Implementierung eines Stacks schauen;) – m654