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 .