我正在解析一个包含函数定义的文件。由于函数可能写成多行,我一直在解析直到遇到 ;
:
#include <iostream>
#include <string>
void removeLineBreaks(std::string &str)
{
auto pos = str.find('\n');
while (pos != std::string::npos)
{
str.replace(pos, 1, "");
pos = str.find('\n', pos);
}
}
int main()
{
std::ifstream ifStream("a.pr");
std::string sLine;
const char sDelim(';');
while (std::getline(ifStream, sLine, sDelim))
{
sLine += sDelim;
removeLineBreaks(sLine);
// process further
}
}
文本可以是这样的:
a=f(b,c); // comment
d=f(e,f);
因为我一直读到 ;
,这里我得到两部分:
a=f(b,c);
和
//注释
\n d=f(e,f);
.
如果我在第二部分调用 removeLineBreaks
,它会变成 //comment d=f(e,f);
所以它会被视为注释通过我的解析器。
我需要哪些选项才能使其正常工作?我能想到的是——在调用removeLineBreaks
之前,获取字符串直到\n
,如果以//
开头,则截断该部分从行中删除,然后才调用 removeLineBreaks
。
还有其他想法吗?
最佳答案
您首先需要从输入中删除 //
注释,然后才能在分号处拆分。
考虑以下输入:
a=f(b,c); // Functions comments are not functions; a=F(b,c);
如果您首先拆分分号,然后删除注释,那么您将得到两个函数:
a=f(b,c);
a=F(b,c);
但你只想拥有第一个。
解决方案是:
- 逐行读取文件(行由 LF 分隔)。
- 同时删除基于行的
//
注释以及所有换行符。 - 将所有输入组合回一个字符串。
- 用分号分隔字符串以提取注释之外的所有函数。
这些步骤不必按顺序进行。您可以对输入的字符流同时执行所有这些步骤,最后发出函数流。事实上,这就是真正的解析器会做的事情。
您实际上是在编写一个简单的解析器。随着您的语言变得越来越复杂,您会发现以这种方式解析文件越来越困难。例如,使用上述方法将很难发出带有行号信息的错误消息。
如果您想编写一个合适的解析器,我会推荐一个递归下降 解析器和一个 PEG(解析器表达式语法)。这种方法很容易学习,比其他方法有更少的缺陷,而且对于计算机语言来说非常强大。看这里:https://en.wikipedia.org/wiki/Parsing_expression_grammar
警告:如果您听到有人建议使用 flex
和 bison
(或 lex 和 yacc),我强烈建议您不要使用它们。它们使用起来很复杂,并且在它们可以解析的内容以及需要指定的方式方面非常有限。我宁愿建议使用像 PEGTL 这样的轻量级现代解析框架:https://github.com/taocpp/PEGTL .
关于c++ - 难以解析后面的评论;在一条线上,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54522371/