استخدام ممتع لـ Ragel State Machine Compiler لإنشاء دالة تحليل سطر على int argc ، char * argv [].
بدأ كل شيء بحقيقة أن وظيفة buildargv كانت مطلوبة لتحليل السلسلة لنقلها لاحقًا إلى
int main (int argc, char *argv[]) { body }
حسنًا ، اعتقدت أنه لا يمكن أن يكون من المستحيل الاقتراض في أي مكان ، والآن نجد ... ولم أجد ...

حسنًا ، ليس أنني لم أجدها على الإطلاق ، على سبيل المثال ، https://github.com/gcc-mirror/gcc/blob/master/libiberty/argv.c (GPLv2 جيدة دائمًا) ، فأنا أفترض على الفور هذه الالتزامات لم يكن جاهزا. هناك بالتأكيد مثل هذه الوظيفة في bash (GPLv3 أفضل). zsh؟ - اذهب تجد (لقد وجدت ... - أنا لا أريد).
بشكل عام ، لم أجد ما أريد ، لكن لم يعجبني ما وجدته. حسنًا ، في النهاية ، لدي الحق في ذلك ، ومع ذلك ، فإنني أجعل لنفسي متعطشًا للترفيه في هذه العملية.
لم أرغب في كتابة هذه الحالة بطريقة تقليدية من الكلمة على الإطلاق ، حتى أنني كنت منزعجًا على هذا الأساس.
بشكل عام ، نلتقي مترجم آلة Ragel State.
أدوات
- دول مجلس التعاون الخليجي.)
- راجل
- جعل
- lcov
- libcheck
يمكن العثور على المشروع هنا: JOYFUL CMDLINE PARSER WRITTEN in RAGEL
بيان المشكلة
عند الإدخال ، لدينا سلسلة من أي نوع ، تتمثل المهمة في الحصول على مجموعة من الوسائط من السلسلة مفصولة بمسافة أو علامة تبويب ، من خلال السلسلة:
- يجب تجاهل أي شخصية تتبع حرف الهروب
\ . - أي شخصيات بين اثنين من الزوجي أو يجب
يعتبر عنصر واحد - في حالة
' مغلق ' أو ' مغلق " ، يتم إرجاع خطأ
بشكل عام ، لا توجد شروط كثيرة. وراجل مناسب جدا لهذه المهمة.
شرح التنفيذ
دعنا نعلن عن جهاز يحمل اسم "buildargv" ونطلب من راجل وضع بياناته في بداية الملف (5.8.1 كتابة البيانات).
%%{ machine buildargv; write data; }%%
بعد ذلك ، نعلن عن جهاز lineElement ، والذي يتكون بدوره من الجمع بين (2.5.1 Union) جهازين: arg و whitespace .
lineElement = arg >start_arg %end_arg | whitespace; main := blineElements**;
عند إدخال وإخراج جهاز arg ، end_arg الإجراءات start_arg و 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_++; }
علاوة على ذلك ، start_arg مهمة start_arg على موضع الحرف عند الإدخال ، end_arg مهمة end_arg عنصرًا جديدًا إلى صفيف argv ، في حالة الخروج الناجح من جهاز arg .
الآن دعونا نلقي نظرة فاحصة على arg .
arg = '\''> { fcall squote; } | '"'>{ fcall dquote; } | ( '\\'>{fcall skip;} | ^[ \t"'\\] )+;
يتكون من اتحاد مكون من ثلاث آلات ' و " و (\ | ^[ \t"'\]) ، والأخير هو بدوره اتحاد \ و ^[ \t"'\] على التوالي.
عندما نعثر على الحرف ' نسميه squote ' ، فإننا نسمي squote ، أو إذا كان الحرف الحالي هو \ ندعو skip ، والذي يتخطى أي حرف squote ، وأي حرف ليس 0x20 (مسافة) ، 0x09 (علامة تبويب) ، ' ، " أو \ يعتبر صحيحاً .
يبقى للنظر في جزء صغير جدا:
skip := any @{ fret; }; dquote := ( '\\'>{ fcall skip; } | ^[\\] )+ :> ["] @{ fret; } @err(dquote_err); squote := ( '\\'>{ fcall skip; } | ^[\\] )+ :> ['] @{ fret; } @err(squote_err);
باستخدام skip اكتشفنا بالفعل ما الذي يجب ألا يتسبب ^['\\] طرح أسئلة عليه. وهنا :> هذا هو Entry-Guarded Concatenation (4.2 مشغلين محميين يقومون بتغليف الأولويات) ، معناه أن الجهاز ( '\\'>{ fcall skip; } | ^['\\] )+ يكمل التنفيذ عندما ["] يعود إلى الحالة الأولية.
وأخيرًا ، في حالة حدوث خطأ في نهاية السطر مع علامات اقتباس مفتوحة ، squote_err dquote_err و squote_err للإشارة إلى رمز الخطأ المقابل وتعيينه.
action dquote_err { ret = -1; errsv = BUILDARGV_EDQUOTE; } action squote_err { ret = -1; errsv = BUILDARGV_ESQUOTE; }
يتم تنفيذ كود الشفرة بواسطة الأمر:
ragel -e -L -F0 -o buildargv.c buildargv.rl
يمكن العثور على قائمة خطوط الاختبار في test_cmdline.c .
استنتاج
تم حل المشكلة.
هل كان أسرع؟ أنا أشك في ذلك. أكثر وضوحا؟ إذا كنت فقط خبيرا في راجل.
أنا لا أدعي الاستبداد ، وسأكون ممتنا لتعليقات بناءة على مدونة راجل.
قائمة المواد:
[^ 1]: أدريان ثورستون. راجل الدولة آلة مترجم .