Fungsi Buildargv dengan Ragel

Penggunaan menyenangkan Kompilator Mesin Negara Ragel untuk membuat fungsi parsing garis pada int argc, char * argv [].


Semuanya dimulai dengan fakta bahwa fungsi buildargv diperlukan untuk mengurai string untuk transfer selanjutnya


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

Yah, saya pikir, tidak mungkin bahwa tidak mungkin untuk meminjam di mana pun, sekarang kita menemukan ... Dan saya tidak menemukan ...



Yah, bukannya saya tidak akan menemukannya sama sekali, misalnya, https://github.com/gcc-mirror/gcc/blob/master/libiberty/argv.c (GPLv2 selalu baik), saya langsung mengambil kewajiban seperti itu belum siap. Pasti ada fungsi seperti itu di bash (GPLv3 bahkan lebih baik). zsh? - Pergi mencari (saya menemukan ... - Saya tidak mau).


Secara umum, saya tidak menemukan apa yang saya inginkan, tetapi saya tidak menyukai apa yang saya temukan. Ya, pada akhirnya saya punya hak untuk melakukannya, sama saja, saya membuat saya sendiri haus akan hiburan dalam prosesnya.


Saya tidak ingin menulis kasus ini dengan cara konvensional dari kata sama sekali, saya bahkan kesal dengan alasan ini.


Secara umum, kami bertemu Ragel State Machine Compiler.


Toolkit


  • gcc;)
  • kain perca
  • membuat
  • lcov
  • libcheck

Proyek ini dapat ditemukan di sini: CMDLINE PARSER JOYFUL TERTULIS DI RAGEL


Pernyataan masalah


Pada input, kami memiliki string dalam bentuk apa pun, tugasnya adalah mendapatkan dari argumen array yang dipisahkan oleh spasi atau tab, dengan:


  • Setiap karakter yang mengikuti karakter pelarian \ harus diabaikan.
  • Setiap karakter yang berada di antara dua ganda atau harus
    dianggap satu elemen
  • Dalam hal ' atau ' tertutup, kesalahan akan dikembalikan

Secara umum, tidak ada banyak kondisi. Dan Ragel sangat cocok untuk tugas ini.


Implementasi yang Dijelaskan


Mari mendeklarasikan mesin dengan nama "buildargv" dan meminta Ragel untuk menempatkan datanya di awal file (5.8.1 Tulis Data).


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

Selanjutnya, kami mendeklarasikan mesin lineElement , yang pada gilirannya terdiri dari menggabungkan (2.5.1 Union) dua mesin: arg dan whitespace .


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

Pada input dan output dari mesin arg , masing-masing tindakan start_arg dan 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_++; } 

Selain itu, tugas start_arg untuk menyimpan posisi karakter pada input, dan tugas end_arg menambahkan elemen baru ke array argv , jika berhasil keluar dari mesin arg .


Sekarang mari kita melihat lebih dekat pada arg .


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

Terdiri dari penyatuan tiga mesin ' , " dan (\ | ^[ \t"'\]) , yang terakhir pada gilirannya adalah penyatuan masing-masing dari \ dan ^[ \t"'\] .


Ketika kita menemukan karakter ' kita panggil squote , ' kita sebut squote , atau jika karakter saat ini \ sebut skip , yang melompati setiap karakter yang mengikutinya, dan karakter apa pun tidak 0x20 (spasi), 0x09 (tab), ' , " atau \ dianggap benar .


Masih mempertimbangkan bagian yang sangat kecil:


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

Dengan skip kita sudah tahu apa yang harus dilakukan ^['\\] juga tidak menimbulkan pertanyaan. Dan di sini :> ini adalah Entry-Guarded Concatenation (4.2 Operator yang dijaga yang merangkum Prioritas) artinya adalah mesin ( '\\'>{ fcall skip; } | ^['\\] )+ menyelesaikan eksekusi ketika ["] kembali ke keadaan awal.


Dan akhirnya, dalam kasus kesalahan end-of-line dengan tanda kutip terbuka, dquote_err dan squote_err untuk menunjukkan dan mengatur kode kesalahan yang sesuai.


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

Pembuatan kode dilakukan oleh perintah:


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

Daftar garis uji dapat ditemukan di test_cmdline.c .


Kesimpulan


Masalahnya terpecahkan.


Apakah lebih cepat? Saya meragukannya. Lebih jelas? Andai Anda seorang ahli Ragel.


Saya tidak berpura-pura absolutisme, saya akan berterima kasih atas komentar konstruktif pada kode Ragel.


Daftar bahan:


[^ 1]: Adrian Thurston. Kompiler Mesin Negara Ragel .

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


All Articles