递归下降与递归上升解析

时间:2011-11-13 14:47:01

标签: c++ parsing lalr

如果我正在编写自己的自定义解析器,我怎么知道我是否正在编写一个递归上升解析器?我肯定对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
}

1 个答案:

答案 0 :(得分:3)

LL和LALR都是O(n),所以没关系。

但是,并非所有递归下降解析器都是LL。那些没有使用某种形式的回溯的人 - 尝试一种制作,当它不能用于尝试另一种制作,直到所有可能的制作都已用完或找到成功的解析。注意到你这样做并不是很难: - )

至于你如何知道你是在构建一个LL还是LALR解析器 - 你知道你正在遵循哪种构造方法。

编辑补充:递归下降和递归上升之间的一个显着特征是程序的作用。在递归下降中,您有一个针对每个非终结符的过程。在递归上升中,每个LR状态都有一个过程。为了拥有后者,你几乎必须预先构建LR自动机(除非你经常这样做,你可以动态地做 - 但在这种情况下你不会问这个问题)。你的第一个代码示例看起来像递归下降;但你没有告诉我们第二个代码示例如何与你的语法相关,因此很难说出来。