递归下降与递归上升解析
Recursive descent vs recursive ascent parsing
本文关键字:递归 更新时间:2023-10-16
如果我正在编写自己的自定义解析器,我如何知道我是否正在编写递归上升解析器?我确实对LALR解析的O(n)复杂性感兴趣(加上我已经有了LALR语法),并且不想以后发现我已经编写了一个LL解析器。
编辑:我只见过自动表驱动解析器和几个生成的简单递归解析器示例-它们看起来都不像我手工构建的任何东西。因此,很难将处理规则的"明显"代码与实际算法联系起来。
如果您将代码用于一个相对简单的规则,例如
name_or_qualified_name = identifier *('.' identifier);
我把它翻译成
std::vector<std::wstring> RecursiveParseNameOrQualifiedName(Iterator& begin, Iterator end) {
std::vector<std::wstring> retval;
retval.push_back(begin->Codepoints);
CheckedIncrement(begin, end); // The only place that can legitimately expect end of input is namespace contents.
while(begin->type == Wide::Lexer::TokenType::Dot) {
CheckedIncrement(begin, end);
if (begin->type != Wide::Lexer::TokenType::Identifier)
Wide::ParserExceptionBuilder(*begin) << L"Expected 'identifier' after '.'";
retval.push_back(begin->Codepoints);
}
return retval;
}
没有什么非常左或右的。这显然是有用且重要的信息,但我没有看到。这里唯一明显的事实是它是递归的。
编辑:对不起,坏的例子。不如这样写:
void RecursiveParseUsing(Iterator& begin, Iterator end, Wide::Parser::NamespaceAST* current_namespace) {
auto new_using = std::unique_ptr<Wide::Parser::UsingAST>( new Wide::Parser::UsingAST() );
// expect "begin" to point to a using
CheckedIncrement(begin, end);
// Must be an identifier, at least
if (begin->type != Wide::Lexer::TokenType::Identifier)
Wide::ParserExceptionBuilder(*begin) << L"Expected 'identifier' after 'using'";
CheckedIncrement(begin, end);
switch(begin->type) {
case Wide::Lexer::TokenType::Dot: {
begin--; // back to identifier
new_using->original_name = RecursiveParseNameOrQualifiedName(begin, end);
current_namespace->unnamed_contents.push_back(std::move(new_using));
break; }
case Wide::Lexer::TokenType::Equals: {
begin--; // back to Identifier
new_using->new_name = begin->Codepoints;
begin++; // Back to equals
begin++; // The token ahead of equals- should be "identifier"
new_using->original_name = RecursiveParseNameOrQualifiedName(begin, end); // The only valid next production
// this should be left at the one past the name
current_namespace->contents[new_using->new_name] = std::move(new_using);
break; }
case Wide::Lexer::TokenType::Semicolon: {
begin--; // Identifier
new_using->original_name.push_back(begin->Codepoints);
begin++; // Semicolon
current_namespace->unnamed_contents.push_back(std::move(new_using));
break; }
default:
Wide::ParserExceptionBuilder(*begin) << L"Expected '.', '=' or ';' after 'identifier' when parsing 'using'.";
}
if (begin->type != Wide::Lexer::TokenType::Semicolon)
Wide::ParserExceptionBuilder(*begin) << L"Expected ';' after 'identifier'";
CheckedIncrement(begin, end); // One-past-the-end
}
LL和LALR都是0 (n),所以没有关系。
然而,并非所有递归下降解析器都是LL。那些没有使用某种形式的回溯- & & &;尝试一种产品,当它不起作用时,尝试另一种产品,直到用尽所有可能的产品或找到成功的解析。注意到您正在这样做并不难:-)
关于你如何知道你是在构造一个LL还是LALR解析器–您可以通过注意您正在使用的构造方法来了解它。
编辑补充:递归下降和递归上升之间的一个显著特征是程序的作用。在递归下降中,每个非终结符都有一个过程。在递归上升中,每个LR状态都有一个过程。为了得到后者,你几乎必须事先构造LR自动机(除非你经常这样做,以至于你可以在飞行中完成)。但那样的话,你就不会问这个问题了)。你的第一个代码示例看起来像递归下降;但是你没有告诉我们第二个代码示例是如何与你的语法相关联的,所以很难告诉我们。
相关文章:
- 通过递归进行因子分解
- 递归函数计算序列中的平方和(并输出过程)
- 使用递归的数组的最小值.这是怎么回事
- 递归列出所有目录中的C++与Python与Ruby的性能
- 递归计数给定目录的文件和所有目录
- 如何在BST的这个简单递归实现中消除警告
- C++:正在检查LinkedList中的回文-递归方法-错误
- 递归模板化函数不能分配给具有常量限定类型"const tt &"的变量"state"
- 递归无序映射
- TSP递归解的迭代形式
- 如何在Elixir中调用递归函数并行
- 返回递归调用和仅递归调用的区别
- 数组元素打印的递归方法
- 使用递归时获取变量的奇怪值
- 如何在C++中递归地按相反顺序打印集合
- 到连接组件算法的问题(递归)
- 如何使用递归打印修改后的星号三角形图案
- 使用递归模板动态分配的多维数组
- 递归函数有效,但无法记忆
- 包含模板文件的递归会导致编译失败