配置解析库是否已存在,将读取以下样式的文件:
Keyword Label Value;
嵌套{ }
替换值;可选标签;支持“包含”会很不错。
示例配置文件可能如下所示:
Listen Inside 127.0.0.1:1000;
Listen Outside {
IP 1.2.3.4;
Port 1000;
TLS {
CertFile /path/to/file;
};
};
ACL default_acl {
IP 192.168.0.0/24;
IP 10.0.0.0/24;
};
答案 0 :(得分:2)
您熟悉哪些编程语言?我对你问题的印象是C。
看起来像配置语言的标记是正则表达式:
几乎所有现代编程语言都有某种形式的支持。
如果实现是C,我可能会使用flex。它生成一个函数,它将应用一组正则表达式,将匹配的文本放入C字符串,并返回该正则表达式的类型(只是一个int,您可以选择)。该功能是一个词法分析器'或者' tokeniser'。它将字符串切换成符合您需求的便捷单元,一次一个正则表达式。
Flex很容易使用。它比lex有几个优点。一个是你可以有多个词法分析器功能,所以如果你需要为包含文件做一些奇怪的事情,那么你可以为这个工作设置第二个词法分析器功能。
您的语言看起来很简单。 Bison / Yacc是非常强大的工具,并且具有强大的功能带来了巨大的责任感。 :-)
我认为这很简单,我可能只是手工编写一个解析器。处理其结构可能只是一些功能。一种非常简单的技术称为递归下降解析器。你有CS学位,还是了解这些东西?
很多人会(在这个阶段)告诉你得到' Dragon Book'或者是其中一个较新的版本,通常是因为这是他们在大学时所拥有的。龙书很棒,但它就像告诉别人阅读维基百科的所有内容以了解鲸鱼。如果你有时间,那就太棒了,你会学到很多东西。
合理的开始是Wikipedia Recursive Descent parser文章。递归下降非常受欢迎,因为它相对简单易懂。使它变得简单的事情就是拥有一个适当的语法,这种语法被转换成一种易于递归下降解析的形式。然后你会为每个规则编写一个函数,并使用一个简单的错误处理机制(这就是为什么我问这个问题)。可能有生成它们的工具,但您可能会发现编写它的速度更快。第一次削减可能需要一天,然后你就可以做出决定。
一个非常漂亮的lex / flex功能是任何不匹配的字符,只是回显标准输出。因此,您可以看到正则表达式匹配的内容,并可以逐步添加它们。当输出“干涸”时一切都在匹配。
宗座警报:恕我直言,更多的C程序员应该学会使用flex。它相对易于使用,并且非常强大,适用于文本处理。恕我直言,因为他们也被告知使用yacc / bison,这是更强大,更微妙和复杂的工具。 结束宗座。
如果您需要一些语法帮助,请询问。如果有一个很好的语法(可能不是这样,但到目前为止你的例子看起来还不错)那么实现很简单。
我找到了两个有关stackoverflow答案的链接,看起来很有帮助:
以下是使用flex的示例。
Flex采用'脚本',并生成一个名为yylex()的C函数。这是输入脚本。
请记住,所有正则表达式都在yylex函数中匹配,所以尽管脚本看起来很奇怪,但它实际上是一个普通的C函数。要告诉调用者,这将是你的递归下降解析器,匹配什么类型的正则表达式,它返回一个你选择的整数值,就像任何普通的C函数一样。
如果没有什么可以告诉解析器,如空格,可能还有某种形式的注释,它就不会返回。它默默地'消耗这些字符。如果语法需要来使用换行符,那么它将被识别为令牌,并将合适的令牌值返回给解析器。有时候更容易让它更自由,所以这个例子消耗并忽略所有的空格。
有效地,yylex函数是从第一个%%
到第二个%%
的所有内容。它表现得像一个很大的switch()
语句。
正则表达式就像(非常奇特的)case:
标签。
{ ... }
中的代码是普通的C.它可以包含任何C语句,并且必须正确嵌套在{ ... }
第一个%%
之前的内容是放置弹性定义的地方,以及一些'指令'弯曲。
%{ ... %}
中的内容是普通的C,可以包含文件中C所需的任何头,甚至可以定义全局变量。
第二个%%
之后的内容是普通的C,不需要额外的语法,所以没有%{ ... %]
。
/* scanner for a configuration files */
%{
/* Put headers in here */
#include <config.h>
%}
%%
[0-9]+ { return TOK_NUMBER; }
[0-9]+"."[0-9]+"."[0-9]+"."[0-9]+":"[0-9]+ { return TOK_IP_PORT; }
[0-9]+"."[0-9]+"."[0-9]+"."[0-9]+"/"[0-9]+ { return TOK_IP_RANGE; }
"Listen" { return TOK_KEYWORD_LISTEN; }
[A-Za-z][A-Za-z0-9_]* { return TOK_IDENTIFIER; }
"{" { return TOK_OPEN_BRACE; }
"}" { return TOK_CLOSE_BRACE; }
";" { return TOK_SEMICOLON; }
[ \t\n]+ /* eat up whitespace, do nothing */
. { fprintf(stderr, "Unrecognized character: %s\n", yytext );
exit(1);
}
%%
/* -------- A simple test ----------- */
int main(int argc, char *argv[])
{
int tok;
yyin = stdin;
while (tok=yylex()) {
fprintf(stderr, "%d %s\n", tok, yytext);
}
}
它有一个最小的虚拟主,它调用yylex()函数来获取下一个标记 (枚举)价值。 yytext是正则表达式匹配的字符串,因此main只打印它。
警告,这几乎没有经过测试,仅仅是:
flex config.l
gcc lex.yy.c -ll
./a.out <tinytest
值只是整数,因此标题中的枚举:
#ifndef _CONFIG_H_
#define _CONFIG_H_
enum TOKENS {
TOK_KEYWORD_LISTEN = 256,
TOK_IDENTIFIER = 257,
TOK_OPEN_BRACE = 258,
TOK_CLOSE_BRACE = 259,
TOK_SEMICOLON = 260,
TOK_IP_PORT = 261,
TOK_IP_RANGE = 262,
TOK_NUMBER = 263,
};
#endif _CONFIG_H_
在解析器中,在需要下一个值时调用yylex。在将令牌类型值交还给解析器之前,您可能会将yylex包装在复制yytext的内容中。
你需要舒服地处理记忆。如果这是一个大文件,可以使用malloc来分配空间。但是对于小文件,并且为了使其易于上手和调试,编写自己的“笨蛋”可能是有意义的。分配器。一个哑巴&#39;内存管理系统,可以使调试更容易。最初只有一个静态分配的大字符数组和一个mymalloc()分发片段。我可以想象配置数据永远不会自由()&#39; d。最初可以用C字符串保存所有内容,因此调试很简单,因为输入的确切顺序在char数组中。一个改进的版本可能会&#39; stat&#39;一个文件,并分配足够大的一块。
您如何处理实际配置值有点超出我的描述。可能只需要文本字符串,或者可能已经有了一种机制。通常不需要存储“关键字”的文本值,因为解析器已经识别出它的含义,并且程序可能会转换其他值,例如, IP地址,进入一些内部表示。
答案 1 :(得分:1)
你看过lex and yacc(或者是flex和bison)吗?它有点毛茸茸,但我们使用它来解析看起来与配置文件完全相同的文件。您可以使用括号定义子结构,使用相同的键解析可变长度列表等。
标签是指意见吗?您可以定义自己的注释结构,我们使用'#'表示注释行。
它不支持包括AFAIK。
答案 2 :(得分:0)