Buildargv-Funktion mit Ragel

Unterhaltsame Verwendung des Ragel State Machine Compilers zum Erstellen einer Zeilenanalysefunktion für int argc, char * argv [].


Alles begann damit, dass die buildargv-Funktion benötigt wurde, um den String für die spätere Übertragung zu analysieren


int main (int argc, char *argv[]) { body } 

Nun, ich dachte, es kann nicht sein, dass es unmöglich war, irgendwo zu leihen, jetzt finden wir ... Und ich habe nicht gefunden ...



Nun, nicht dass ich es überhaupt nicht gefunden hätte, zum Beispiel https://github.com/gcc-mirror/gcc/blob/master/libiberty/argv.c (GPLv2 ist immer gut), ich übernehme solche Verpflichtungen sofort war noch nicht fertig. Es gibt definitiv eine solche Funktion in Bash (GPLv3 ist noch besser). zsh? - Geh finden (ich fand ... - ich will nicht).


Im Allgemeinen habe ich nicht gefunden, was ich wollte, aber mir hat nicht gefallen, was ich gefunden habe. Nun, am Ende habe ich das Recht dazu, trotzdem mache ich mir dabei einen Durst nach Unterhaltung.


Ich wollte diesen Fall nicht auf konventionelle Weise aus dem Wort schreiben, ich war sogar verärgert über diesen Grund.


Im Allgemeinen treffen wir den Ragel State Machine Compiler.


Toolkit


  • gcc;)
  • Nagel
  • machen
  • lcov
  • libcheck

Das Projekt ist hier zu finden: JOYFUL CMDLINE PARSER WRITTEN IN RAGEL


Erklärung des Problems


Bei der Eingabe haben wir eine beliebige Zeichenfolge. Die Aufgabe besteht darin, aus der Zeichenfolge ein Array von Argumenten zu erhalten, die durch ein Leerzeichen oder einen Tabulator getrennt sind.


  • Alle Zeichen, die auf das Escape-Zeichen \ folgen, müssen ignoriert werden.
  • Alle Zeichen, die zwischen zwei Doppel- oder Musszeichen liegen
    als ein Element betrachtet werden
  • Bei nicht abgeschlossenem ' oder " wird ein Fehler zurückgegeben

Im Allgemeinen gibt es nicht viele Bedingungen. Und Ragel ist für diese Aufgabe durchaus geeignet.


Erklärte Implementierung


Deklarieren wir eine Maschine mit dem Namen "buildargv" und fordern Ragel auf, ihre Daten am Anfang der Datei zu platzieren (5.8.1 Write Data).


 %%{ machine buildargv; write data; }%% 

Als nächstes deklarieren wir eine lineElement Maschine, die wiederum aus der Kombination (2.5.1 Union) zweier Maschinen besteht: arg und whitespace .


 lineElement = arg >start_arg %end_arg | whitespace; main := blineElements**; 

Am Ein- und Ausgang der arg Maschine werden die Aktionen start_arg und end_arg .


 action start_arg { argv_s = p; } action end_arg { nargv = (char**)realloc((*argv), (argc_ + 1)*sizeof(char*)); (*argv) = nargv; (*argv)[argc_] = strndup(argv_s, p - argv_s); argc_++; } 

Darüber hinaus start_arg die Task start_arg zum Speichern der Position des Zeichens am Eingang und die Task end_arg bei erfolgreichem Beenden des arg end_arg ein neues Element zum argv Array hinzu.


Schauen wir uns nun arg genauer an.


 arg = '\''> { fcall squote; } | '"'>{ fcall dquote; } | ( '\\'>{fcall skip;} | ^[ \t"'\\] )+; 

Es besteht aus einer Vereinigung von drei Maschinen ' , " und (\ | ^[ \t"'\]) , wobei letztere wiederum eine Vereinigung von \ und ^[ \t"'\] .


Wenn wir das Zeichen ' We call squote ' , rufen wir squote , oder wenn das aktuelle Zeichen \ we call squote , wird jedes darauf folgende Zeichen 0x09 , und jedes Zeichen ist nicht 0x20 (Leerzeichen), 0x09 (Tabulator), ' , " oder \ wird als korrekt angesehen .


Es bleibt ein sehr kleiner Teil zu berücksichtigen:


 skip := any @{ fret; }; dquote := ( '\\'>{ fcall skip; } | ^[\\] )+ :> ["] @{ fret; } @err(dquote_err); squote := ( '\\'>{ fcall skip; } | ^[\\] )+ :> ['] @{ fret; } @err(squote_err); 

Mit skip wir bereits herausgefunden, was ^['\\] auch keine Fragen verursachen soll. Und hier :> Dies ist Entry-Guarded Concatenation (4.2 Guarded Operators, die Prioritäten ( '\\'>{ fcall skip; } | ^['\\] )+ ). Seine Bedeutung ist, dass die Maschine ( '\\'>{ fcall skip; } | ^['\\] )+ die Ausführung abschließt, wenn ["] kehrt in den Ausgangszustand zurück.


Und schließlich werden im Fall eines Zeilenende-Fehlers mit offenen Anführungszeichen dquote_err und squote_err , um den entsprechenden Fehlercode dquote_err und squote_err .


 action dquote_err { ret = -1; errsv = BUILDARGV_EDQUOTE; } action squote_err { ret = -1; errsv = BUILDARGV_ESQUOTE; } 

Die Codegenerierung erfolgt mit dem Befehl:


 ragel -e -L -F0 -o buildargv.c buildargv.rl 

Eine Liste der test_cmdline.c finden Sie in test_cmdline.c .


Fazit


Das Problem ist gelöst.


War es schneller Ich bezweifle es. Klarer? Wenn Sie nur ein Experte für Ragel sind.


Ich täusche keinen Absolutismus vor, ich werde für konstruktive Kommentare zum Ragel-Code dankbar sein.


Materialliste:


[^ 1]: Adrian Thurston. Ragel State Machine Compiler .

Source: https://habr.com/ru/post/de477296/


All Articles