استخدام ممتع لـ 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]: أدريان ثورستون. راجل الدولة آلة مترجم .