1unitedpower: multipart/form-data

Beitrag lesen

Ein echter Serializer macht es im konkreten Fall so (deserialize):

  • Länge des mitgelieferten Boundary-String ermitteln ($blen = length $boundary_string)
  • lese $blen aus STDIN, stehe danach vor einem Zeilenumbruch,
  • lese nun in Schritten von genau 1 byte weiter um den nächsten Zeilenumbruch zu finden,
  • wenn ich den Zeilenumbruch habe, parse ich die bisherigen Daten name=; filename=; Content-Type...
  • erstelle nun ein tmp/-Handle, lese aus STDIN in Schritten von 1 Byte weiter und kopiere die gelesenen Bytes in das /tmp-Handle
  • beende das Lesen aus STDIN, wenn ich im Puffer den Boundary-String entdecke,
  • schneide den Boundary-String vom /tmp-Handle ab
  • sichere die gewonnenen Daten in meinem Parser-Objekt
  • wiederhole den hier beschriebenen Algorithnmus solange, bis keine Daten mehr kommen.

Okay, da haben wir doch was Greifbares zum drüber reden. Ich sehe da noch ziemlich viel Spiel für Verbesserungen, in absteigender Priorität:

Zunächst hardcodest du die Grammatikregeln deines Parsers. Das führt zu verwobenen Code, über den nur mühevoll argumentiert werden kann. Das kannst du vermeiden, indem du die Grammatikregeln als Eingabe des Parsers auffasst anstatt sie fest im Quelltext zu verankern. Dann hast du einen abstrakten Parser, den du mit verschiedenen Grammatiken füttern kannst, und der dir als Ausgabe einen abstrakten Syntaxbaum liefert.

Wie schon in meinem ersten Posting erwähnt, mischst du lexikalische und syntaktische Analyse miteinander. Dazu kann ich dir nur den selben Rat wieder erteilen: Benutze einen Tokenizer als vorbereitende Maßnahme für den Parser. In Kombination mit dem ersten Vorschlag, löst das auch gleich zwei deiner Probleme: Du musst dir keine Gedanken mehr darüber machen, mit wievielen Bindestrichen eine Boundary beginnt, und wie du an die Länge eines Blocks zwischen den Boundaries gelangst. Du kannst Nebensächlichkeiten ausblenden und das große Bild im Auge behalten.

Außerdem haben Parser naturgemäß die Eigenheit, dass sie viele Fallunterscheidungen benötigen. Es ist eine süße Versuchung diese direkt mit verschachtelten if-Verzweigungen zu modellieren, allerdings wird das irgendwann unübersichtlich. Deshalb macht es Sinn diesem Aspekt mit einer strukturierten Lösung zu begegnen, endliche Automaten haben sich für kontextfreie Grammatiken als gutes Werkzeug etabliert.

Die verschiedenen Phasen eines Parsers (lesen, lexikalische Analyse, syntaktische Analyse und Ausgabe schreiben) lassen sich zuletzt auch sehr gut parallelisieren. Deshalb bietet es sich an, die Komponenten mit zeitdiskreten Ein- und Ausgabeströmen zu verknüfen, anstatt mit punktuellen Daten. Das führt imho. zu besonders eleganten Interfaces.

Zumindest die ersten drei Punkte sind ganz elementare Grundlagen bei der Entwicklung von Parsern, die in vielen Textbüchern sehr anschaulich behandelt werden.