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:
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 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?
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:
Tags und Links haskell monads monad-transformers