Buildargv وظيفة مع راجل

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

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


All Articles