带有Ragel的Buildargv函数

有趣地使用Ragel状态机编译器在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状态机编译器。


工具包


  • gcc;)
  • 拉格尔
  • 使
  • lcov
  • libcheck

该项目可以在这里找到: RAGEL中编写的JOYFUL CMDLINE PARSER


问题陈述


在输入处,我们有任何类型的字符串,任务是从字符串中获取由空格或制表符分隔的参数数组,其中包括:


  • 转义字符\之后的任何字符都必须忽略。
  • 在两个双打之间或必须在两个双打之间的任何字符
    被视为一个要素
  • 如果未封闭'" ,则应返回错误

通常,条件不多。 Ragel非常适合此任务。


解释执行


声明一台名为“ buildargv”的计算机,并要求Ragel将其数据放在文件的开头(5.8.1写数据)。


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

接下来,我们声明一个lineElement机器,该机器依次由两个机器( argwhitespace )组合(2.5.1 Union)组成。


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

arg机器的输入和输出处end_arg分别end_arg动作start_argend_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_++; } 

此外,在成功退出arg机器的情况下,任务start_arg将字符的位置保存在输入中,而任务end_arg将新元素添加到argv数组中。


现在,让我们仔细看一下arg


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

它由三个机器'"(\ | ^[ \t"'\])的联合组成,后者又分别是\^[ \t"'\]


当找到字符'我们称为squote '我们称为squote ,或者如果当前字符为\称为skip ,它将跳过其后的任何字符,并且任何字符都不为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_errsquote_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找到。


结论


问题解决了。


它更快吗? 我对此表示怀疑。 更清楚吗? 只要您是Ragel的专家。


我不假装专制主义,对于Ragel代码的建设性评论,我将不胜感激。


材料清单:


[^ 1]: Adrian Thurston。 Ragel状态机编译器

Source: https://habr.com/ru/post/zh-CN477296/


All Articles