不幸的是,您没有提供太多有关要解析的信息。如果您给出(请编辑您的问题),那么我将为您编写一个示例解析器。
目前,我只能给你关于 Parsers 的一般解释。
这完全取决于您的数据的结构。您需要了解能够表达信息的 ASCII 表示的形式语言和语法。还有所谓的乔姆斯基层次结构,它对语言进行分类并描述了实现解析器的方式。
您对
的声明
我天真的第一个客人是简单地对关键字进行字符串比较 if(!strcmp(...)) ,然后按位置拆分字符串以获取信息。
如果您的数据是所谓的 Chomsky Type-3 正则语言,则可以使用。您不会使用 strcmp() 或其他 C 函数,而是使用 std::regex 来匹配 ASCII 文本中的模式,然后返回一些结果,用属性着色。
但是你的例子
.START_CMD
信息1 信息2 信息3
*额外的nal_info1 ...
.END
表示您有一些带有复合说明符的嵌套数据。 Chomsky Type-3 常规语言无法表达这一点。正则表达式,通常实现为 DFA(确定性有限自动机)无法计数。他们没有记忆。只知道他们目前的状态。因此它们无法将某些“开始”语句的数量与“结束”语句相匹配。这是不可能的。
您需要一种语法,最好是上下文无关语法 (CFG) 来描述这种语言。解析器将使用下推自动机来实现。您将使用“解析堆栈”。这个堆栈将保存所有附加信息。那是正则表达式所没有的记忆。
在我看来,这种方法足以满足您的目的
现在,如何实现它。有几种选择:
- Lex/Flex Yacc/Bison:非常强大。难以理解和实施
- 提升精神:同上。也需要一些时间来理解
- 手工解析器:很多工作。
如果您从手工制作的解析器开始,您将学到最多并理解解析的工作原理。我将在我的解释中继续这一点。
标准解决方案是 Shift/Reduce Parser。
你需要一个带有产生式(通常是动作)的语法
您将需要 TokenTypes 来查找、读取和使用输入数据的 Lexem。这通常通过正则表达式匹配来实现。
然后您将需要带有属性的令牌。 Scanner/Lexer,或简单的函数 getToken,将读取输入文本并“标记化”它。然后它将带有属性的令牌(一个属性例如一个整数的值)返回给解析器。
解析器将令牌压入堆栈。然后它尝试将堆栈的顶部与产生式的右侧匹配。如果匹配,则堆栈会减少产生式右侧的元素数量,并替换为产生式左侧的无终端。并调用了一个 Action。
重复此操作,直到所有输入都匹配或检测到语法错误。
我现在将向您展示一些(未编译。未测试)伪代码
#include <vector>
#include <string>
#include <variant>
#include <functional>
#include <iostream>
// Here we store token types for Terminals and None-Terminals
enum class TokenType {END, OK, EXPRESSION, START1, END1, START2, END2, INTEGER, DOUBLE, STRING};
struct TokenWIthAttribute {
TokenWIthAttribute(const TokenType &tt) : tokenType(tt) {}
TokenWIthAttribute(const TokenWIthAttribute &twa) : tokenType(twa.tokenType) {}
TokenType tokenType{};
std::variant<int, double, std::string> attribute{};
bool operator ==(const TokenWIthAttribute& twa) const { return tokenType == twa.tokenType;}
};
using NonTerminal = TokenType;
using Handle = std::vector<TokenWIthAttribute>;
using Action = std::function<TokenWIthAttribute(TokenWIthAttribute&)>;
struct Production {
NonTerminal nonTerminal{}; //Left side of Production
Handle handle{}; //Rigth side of prodcution
Action action; //Action to take during reduction
};
using Grammar = std::vector<Production>;
TokenWIthAttribute actionEndOK(TokenWIthAttribute& twa) {
// Do something with twa
return twa;
}
Grammar grammar{
{ TokenType::OK, {TokenType::START1, TokenType::EXPRESSION, TokenType::END1, TokenType::END},actionEndOK}
// Many lines of more productions
};
using ParseStack = std::vector<TokenWIthAttribute>;
class Parser
{
public:
bool parse(std::istream &is);
protected:
TokenWIthAttribute getToken(std::istream &is);
void shift(TokenWIthAttribute& twa) { parseStack.push_back(twa); }
bool matchAndReduce();
ParseStack parseStack;
};
bool Parser::matchAndReduce()
{
bool result{ false };
// Iterate over all productions in the grammar
for (const Production& production : grammar) {
if (production.handle.size() <= parseStack.size()) {
// If enough elements on the stack, match the top of the stack with a production
if (std::equal(production.handle.begin(), production.handle.end(), parseStack.cend() - production.handle.size())) {
// Found production: Reduce
parseStack.resize(parseStack.size() - production.handle.size());
// Call action. Replace right side of production with left side
parseStack.emplace_back(production.action(*(parseStack.begin()+parseStack.size()-1)));
result = true;
break;
}
}
}
return result;
}
int main()
{
std::cout << "Hello World\n";
return 0;
}
我希望这能给您一个第一印象。