Einen Assembler in Haskell schreiben - mapM mit Zustand?

8

Ich schreibe einen sehr einfachen Assembler mit zwei Durchgängen in Haskell und ich bin auf ein Szenario gestoßen, das ich noch nicht zu lösen habe. Ich denke, die Lösung wird wahrscheinlich Monadetransformatoren beinhalten, die ich nicht wirklich verstehe.

Der Assembler analysiert den Assemblycode in eine Liste von Statement s, bei denen es sich entweder um Anweisungen oder Beschriftungen handelt. Einige Statement s beziehen sich möglicherweise auf Etiketten. Der Assembler muss das Statement s in Instruction s konvertieren, was das Eliminieren der Beschriftungen und das Ersetzen der Beschriftungsreferenzen durch einen geeigneten Wert beinhaltet.

Ich habe den ersten Durchlauf des Assemblers geschrieben, der ein [(String, Int)] erzeugt, das eine Karte von Etiketten zu Adressen darstellt. Ich habe auch die folgende Funktion geschrieben, um ein Statement in ein Instruction zu übersetzen:

%Vor%

Ich habe einige Fälle der Kürze halber weggelassen, aber Sie können hier alle möglichen Ergebnisse sehen:

  • ADD ist immer erfolgreich und erzeugt eine Anweisung
  • BEQL kann entweder erfolgreich sein oder fehlschlagen, je nachdem, ob eine Bezeichnung gefunden wurde
  • LABEL ist immer erfolgreich, obwohl es keine tatsächlichen Anweisungen
  • erzeugt

Dies funktioniert wie erwartet. Das Problem, das ich jetzt habe, ist diese Funktion schreiben:

%Vor%

replaceLabels nimmt eine Liste von Anweisungen und führt stmtToInstruction auf jedem aus. Das Argument addr bis stmtToInstruction muss die Länge des bisher akkumulierten [Instruction] sein. Die Ausgabe kann entweder ein Left String sein, wenn eine der Labelreferenzen ungültig war, oder ein Right [I.Instruction] , wenn keine Fehler aufgetreten sind.

mapM :: Monad m => (a -> m b) -> [a] -> m [b] bringt uns einen Teil des Weges dorthin, bietet aber keine Möglichkeit, die aktuelle Adresse in die Funktion (a -> m b) zu injizieren. Wie mache ich das?

    
rossng 18.11.2017, 23:20
quelle

3 Antworten

4

Sie haben Recht: Der StateT Monodentrafo wird den Trick machen:

%Vor%

Aber das Schreiben der spezialisierten Version für Listen könnte besser sein:

%Vor%     
dfeuer 19.11.2017 00:10
quelle
1

Ich habe eine rekursive Lösung implementiert, von der ich sicher bin, dass sie sehr ineffizient ist. Ich wäre immer noch interessiert, den "richtigen" Weg zu sehen, dies zu tun.

%Vor%     
rossng 18.11.2017 23:58
quelle
0

Ich würde anfangen, indem ich

ändere %Vor%

in

%Vor%

Das heißt, die Funktion, die die Adresse übernimmt, in den Right Zweig von Either verschiebt. Der Grund dafür ist, dass Etikettenreferenzfehler scheinbar unabhängig von Adressen sind. Daher ist es besser, Referenzfehler zuerst zu behandeln und sich dann um das Adressmaterial zu kümmern.

Diese Funktion löst die Referenzen auf:

%Vor%

( traverse entspricht mapM , aber es erfordert nur eine Applicative constraint. Sie sind nur aus historischen Gründen verschiedene Funktionen.)

Ok, nachdem wir die Fehler behoben haben, können wir uns jetzt auf die [Int -> [Instruction]] -Liste konzentrieren. Es scheint, dass wir es von links überlagern müssen, während wir eine akkumulierte Adresse tragen, die wir jeder Funktion liefern müssen. Die Funktion mapAccumL ist dafür perfekt geeignet:

%Vor%     
danidiaz 19.11.2017 09:47
quelle