我的一个程序在运行时接受命令(如kill foo
)。可以把它想象成一种特定领域的语言。以下是一些例子:
kill
kill client
exit
但是,允许使用链式命令,并且在命令之前和之后空格不重要,因此以下示例也是有效的:
kill ; say "that was fun"
kill ; kill ; kill;
我目前使用lex / yacc(flex / bison具体)来实现这一点,这引起了很多麻烦。词法分析器在很大程度上取决于上下文(例如,通常不返回空格标记,除非例如在kill
关键字之后)并且具有许多不同的状态。以前的语法有冲突,我真的不喜欢它必须指定的格式(特别是$ 1,$ 2,$ 3,......使用非终端的参数)。此外,bison提供的错误消息(在分析时)有时是准确的,但通常不是(带有可选参数的kill
命令会导致Unexpected $undefined, expected $end or ;
的{{1}}等错误消息,而不是kill clont
)。最后,yacc的C API是残酷的(外部定义了所有地方)。
我不是要求你解决所有上述问题(如果没有办法绕过lex / yacc,我会打开单独的线程,其中包含更具体的描述和代码)。相反,我对lex / yacc的替代品感兴趣。
我的标准如下:
答案 0 :(得分:3)
至于一个非常简单和小的语法,我会考虑手工编写词法分析器/解析器 - 它通常没那么多工作。
几乎所有的Linux发行版都提供了lex / yacc的变体。除此之外,另外两个广泛使用的解析器生成器是lemon和antlr。
答案 1 :(得分:3)
由于您的语言看起来很简单,我建议实现一个标记和解析输入的有限状态机。
一次只读输入一个字符,在空白处进行标记(而不是在带引号的字符串中)。每个“命令”使机器处于一个不同的状态,它解析命令参数。 “;”或“\ n”将机器重置为启动状态。
答案 2 :(得分:1)
我非常喜欢ANTLR,在生产系统中使用过几次。
有些奇怪的是,它在版本2中支持生成C ++代码但不支持C,而在版本3中它支持生成C代码而不支持C ++。我喜欢C ++,所以仍然使用ANTLR v2,但你可能会喜欢v3。对你来说好多了。
许多发行版都有ANTLR v2包,有些还有v3。它的记录相当充分(注意我使用v2;我希望v3在这方面不会更差)。
ANTLR不会“开箱即用”生成超级精彩的解析错误消息。这似乎是大多数通用解析器系统的常见问题,从根本上说并不是一个容易解决的问题。然而,通过一些工作,我看到一些不错的诊断输出来自基于ANTLR的系统(该应用程序有一些逻辑来帮助弄清楚对用户说什么 - 这里ANTLR没有多少魔法)。 / p>
答案 3 :(得分:1)
Lex& amp; amp; amp; Yacc是Lemon解析器。它有相当多的用途,但我没有认真使用它,所以我不能完全确定它的确如此有效。它由SQLite使用。
答案 4 :(得分:1)
您可能需要考虑Ragel。我最近开始使用它,并且一旦你加快速度,我很高兴能与你合作。在您的示例中,您可能会执行类似的操作(注意:未经过测试!):
#include <stdio.h>
#include <string.h>
%%{
machine my_cmd_lang;
action pk { printf("Killing %.*s\n", fpc-mark, mark); }
action mk { mark = fpc; }
k = 'kill'; # creates a machine that doesn't do anything
x = 'exit' @{ printf("Exiting\n"); };
arg = alpha+ >mk; # arg to kill is built in machine 'alpha' 1 or more times
cmd = ((k space arg) @pk space* ';'?) | x;
main := cmd* ;
}%%
%% write data;
int main(int argc, char* argv[]) {
int cs;
char* p = "kill client";
char* pe = p + strlen(p);
char* mark;
%% write init;
%% write exec;
return 0;
}
使用ragel <filename.rl>
通过Ragel运行它,它会吐出<filename.c>
。
答案 5 :(得分:0)