C++ 如何基于CFG验证输入?
考虑一下这种语法:C++ 如何基于CFG验证输入?,c++,algorithm,parsing,context-free-grammar,compiler-theory,C++,Algorithm,Parsing,Context Free Grammar,Compiler Theory,考虑一下这种语法: expr ::= LP expr RP | expr PLUS|MINUS expr | expr STAR|SLASH expr | term term ::= INTEGER|FLOAT 上下文无关语法定义为G=(V,∑,R,S),其中(在本例中): 现在,让我们定义一个名为Parser的小类,它的定义是(代码示例在C++中提供): 出于测试目的,字符串(2+3)*4已标记为以下集合 { TK_LP, TK_INT
expr ::= LP expr RP
| expr PLUS|MINUS expr
| expr STAR|SLASH expr
| term
term ::= INTEGER|FLOAT
上下文无关语法定义为G=(V,∑,R,S)
,其中(在本例中):
现在,让我们定义一个名为Parser
的小类,它的定义是(代码示例在C++中提供):
出于测试目的,字符串(2+3)*4已标记为以下集合
{ TK_LP, TK_INTEGER, TK_PLUS, TK_INTEGER, TK_RP, TK_STAR, TK_INTEGER }
并被用作解析器的输入数据
现在来看最难的部分:算法。据我所知,我在想:
1) 从起始符号向量(LP expr RP
)中获取第一条规则,并将其拆分为单词
2) 检查规则中的第一个单词是否为terminal
如果单词是终端,则将其与第一个标记进行比较。
- 若它们相等,则增加标记索引并移动到规则中的下一个单词
- 如果它们不相等,则保留标记索引并移动到下一个规则
如果单词不是终端,并且在以前的递归中没有使用,则增加标记索引并进入递归解析(传递新规则和非终端单词)
虽然我对该算法不确定,但我仍然尝试实现它(当然,不成功):
1) OutterParse
启动递归的函数:
void Parser::Parse()
{
int startingTokenIndex = 0;
string word = this->startingSymbol;
for (int ruleIndex = 0; ruleIndex < this->ruleNames[word].size(); ruleIndex++)
{
this->parseRecursive(this->ruleNames[word], ruleIndex, startingTokenIndex, "");
}
}
S -> As
s -> + As | epsilon
A -> a | b
void解析器::Parse()
{
int startingTokenIndex=0;
字符串字=此->开始符号;
对于(int ruleIndex=0;ruleIndexruleNames[word].size();ruleIndex++)
{
this->parseRecursive(this->ruleNames[word],ruleIndex,startingTokenIndex,”);
}
}
2) 递归函数:
void Parser::parseRecursive(vector<string> rules, unsigned ruleIndex, unsigned startingTokenIndex, string prevRule)
{
printf("%s - %s\n", rules[ruleIndex].c_str(), this->tokenNames[this->tokenList[startingTokenIndex]].c_str());
vector<string> temp = this->split(rules[ruleIndex], ' ');
vector<vector<string>> ruleWords;
bool breakOutter = false;
for (unsigned wordIndex = 0; wordIndex < temp.size(); wordIndex++)
{
ruleWords.push_back(this->split(temp[wordIndex], '|'));
}
for (unsigned wordIndex = 0; wordIndex < ruleWords.size(); wordIndex++)
{
breakOutter = false;
for (unsigned subWordIndex = 0; subWordIndex < ruleWords[wordIndex].size(); subWordIndex++)
{
string word = ruleWords[wordIndex][subWordIndex];
if (this->isTerm(word))
{
if (this->tokenNames[this->tokenList[startingTokenIndex]] == this->makeToken(word))
{
printf("%s ", word.c_str());
startingTokenIndex++;
} else {
breakOutter = true;
break;
}
} else {
if (prevRule != word)
{
startingTokenIndex++;
this->parseRecursive(this->ruleNames[word], 0, startingTokenIndex, word);
prevRule = word;
}
}
}
if (breakOutter)
break;
}
}
void Parser::parseRecursive(向量规则、无符号规则索引、无符号启动令牌索引、字符串规则)
{
printf(“%s-%s\n”,规则[ruleIndex].c_str(),this->tokenNames[this->tokenList[startingTokenIndex]].c_str());
向量温度=此->拆分(规则[ruleIndex],“”);
矢量规则词;
bool breakouter=错误;
对于(无符号wordIndex=0;wordIndex拆分(临时[wordIndex],“|”);
}
for(无符号wordIndex=0;wordIndexisTerm(字))
{
如果(this->tokenNames[this->tokenList[startingTokenIndex]]==this->makeToken(word))
{
printf(“%s”,word.c_str());
startingTokenIndex++;
}否则{
breakouter=true;
打破
}
}否则{
if(prevRule!=word)
{
startingTokenIndex++;
this->parseRecursive(this->ruleNames[word],0,startingTokenIndex,word);
规则=单词;
}
}
}
如果(断接器)
打破
}
}
我应该对算法进行哪些更改才能使其正常工作?根据您想要实现的一次性解析器或编译器,会使用不同的方法。对于编译器,编译器主要使用LR,用于手动实现LL。
基本上,对于LL,手动实现使用递归下降(对于每个非终端,创建一个实现它的函数)。
例如,对于语法:
S -> S + A | A
A -> a | b
让我们消除左递归和左因式分解(LL语法不适用于左递归):
这种实施是可能的:
void S (void)
{
A ();
s ();
}
void s (void)
{
if (get_next_token (). value! = '+')
return;
A ();
s ();
}
void A (void)
{
token * tok = get_next_token ();
if (tok.value! = 'a' && tok.value! = 'b')
syntax_error ();
}
如果要添加SDD,则继承的属性将通过参数传递,合成的属性将作为输出值传递
评论:
不要一次收集所有令牌,根据需要获取它们:获取下一个令牌()。根据要实现一次性解析器或编译器的内容,使用不同的方法。对于编译器,编译器主要使用LR,用于手动实现LL。
基本上,对于LL,手动实现使用递归下降(对于每个非终端,创建一个实现它的函数)。
例如,对于语法:
S -> S + A | A
A -> a | b
让我们消除左递归和左因式分解(LL语法不适用于左递归):
这种实施是可能的:
void S (void)
{
A ();
s ();
}
void s (void)
{
if (get_next_token (). value! = '+')
return;
A ();
s ();
}
void A (void)
{
token * tok = get_next_token ();
if (tok.value! = 'a' && tok.value! = 'b')
syntax_error ();
}
如果要添加SDD,则继承的属性将通过参数传递,合成的属性将作为输出值传递
评论:
不要一次收集所有代币,根据需要获取:获取下一个代币()。你是想实现你自己的bison/flex还是我遗漏了什么?@W.F.我会说“有点”。然而,AFAIK bison基于语法生成C代码,而我希望保留语法并使用规则进行解析。我担心这可能会涉及stackoverflow…,也称为“龙书”的广泛主题。关于这个主题的经典文本。似乎您需要一个自上而下的解析器()。因此,尝试将解析函数拆分为更小的部分。parsexpr(),ParseInt()。然后,每个函数都可以专用于解析单个规则。看看llvm教程,他们还使用自顶向下的解析器从头开始构建了一个编译器:你是在尝试实现你自己的bison/flex还是我遗漏了什么?@W.F.我会说“有点”。然而,AFAIK bison基于语法生成C代码,而我希望保留语法并使用规则进行解析。我担心这可能会涉及stackoverflow…,也称为“龙书”的广泛主题。关于这个主题的经典文本。似乎您需要一个自上而下的解析器()。因此,尝试将解析函数拆分为更小的部分。parsexpr(),ParseInt()。然后,每个函数都可以专用于解析单个规则。请看llvm教程,其中他们还使用自顶向下的解析器从头构建了编译器:
void S (void)
{
A ();
s ();
}
void s (void)
{
if (get_next_token (). value! = '+')
return;
A ();
s ();
}
void A (void)
{
token * tok = get_next_token ();
if (tok.value! = 'a' && tok.value! = 'b')
syntax_error ();
}