具有语法


10

谁能启发我,为什么尝试回溯产生式和(按顺序)的带有回溯的递归下降解析器不能识别语法形成的语言。小号一个一个小号一个小号一个| 一个一个小号一个小号一个小号一个一个小号一个小号一个 | 一个一个

它似乎只能解析语言单词。{a2ñ | n1个}

我使用具有生产规则的ABNF Parser Generator生成了这样的解析器,例如,S = "a" S "a" / "aa"解析器无法识别单词aaaaaa

我希望它使用生产,直到解析树的终端节点从7左边开始串联的,然后去分析树选择生产小号一个一个来代替,直到树看起来像这样:小号一个小号一个a小号一个一个

   S 
 / | \
a  S  a
 / | \
a  S  a
  / \
 a   a

2
您为什么认为它无法解析此单词?
Yuval Filmus 2014年

@Yuval,我认为它应该解析它,所以我必须丢失一些东西。
meribold 2014年

嗯,这个问题更有意义了。感谢您的编辑!如果您编写的内容是正确的(我没有检查),则生成器似乎存在错误。(或者它是不是你的语法规定,我认为这是不可能的,因为语法是基础和明确的。
拉斐尔

@Raphael,我再次编辑了问题(希望不更改含义)。实际上,我的任务是解释为什么这样的解析器无法识别单词aaaaaa
meribold 2014年

你从哪儿得到那棵树。从该ABNF解析器生成器中获取的资源很少。你给的树没有多大意义。但是该字符串aaaaaa应该解析,但不是。但是aaaa解析。你显然对2的幂是正确的。它仅aa与解析S = "aa" / "a" [S] "a"。您可以追踪解析器的作用吗?
2014年

Answers:


6

这不是一个很大的答案,但是解析树不适合常规注释。

您的语法应该解析字符串 一个一个一个一个一个一个小号一个小号一个 | 一个一个一个一个一个一个一个一个

但是解析树具有以下形式:

      S 
     /|\
    / S \
   / /|\ \
  / / S \ \
 / / / \ \ \
a a a   a a a

或者,如果您喜欢此演示文稿,请在不同的端子上使用端子

     S 
   / | \
  a  S  a
   / | \
  a  S  a
    / \
   a   a

我检查了ABNF解析器生成器似乎不起作用,但是我不知道如何跟踪它的功能。

确实确实使集合 至极不是什么语法定义。{一个2ñ | ñ1个}

围绕一个错误的解析器拥有如此精致的站点有点令人惊讶,该解析器还使用了一种完全不有趣的解析技术。


进一步查看之后:

我想我找到了问题的根源。方括号表示 可选

所以你的语法应该写成 S = "a" S "a" / "aa"S = "a" [S] "a"。然后,它似乎可以正常工作。

但是当以不同的形式拥有两次相同的规则时,系统显然会丢失。我不知道为什么。

我没有找到说明这些语法问题的页面来指定语法。

我仍然认为那辆马车。


1
哎哟。是的 写那个解析树时我不知道在想什么。我将编辑问题并粘贴您的问题。
meribold 2014年

我没有找到另一递归下降,回溯解析器生成一个在线演示在这里,它显示了此规则相同的行为:S ::= 'a'<S>'a' | 'a''a'
meribold

aaaaaa使用时仍无法解析S = "a" S "a" / "aa",但是您似乎对括号是正确的。
meribold 2014年

我看不到探索递归下降,回溯解析器的意义。
2014年

您是对的S = "a" S "a" / "aa"...我测试得太快了,然后单击“生成”而不是“分析”。
2014年

3

s1()小号一个小号一个trues()s2()小号一个一个

考虑aaaaaa再次解析该单词。一方面,解析树将如下所示:

   S 
 / | \
a  S  a
 / | \
a  S  a    <--
 / | \
a  S  a
  / \
 a   a

s()trueS小号一个一个

   S 
 / | \
a  S  a
  / \
 a   a

不过,我倾向于将其视为实现问题,而不是一般回溯递归下降解析器。

#include <iostream>

char* next;    
bool term(char token) {
    if (*next != '\0')
        return *next++ == token;
    else
        return false;
}

bool s();    
bool s1() {
    return term('a') && s() && term('a');
}    
bool s2() {
    return term('a') && term('a');
}    
bool s() {
    auto save = next;
    return s1() or (next = save, s2());
}    

int main(int argc, char* argv[]) {
    next = "aaaaaa";
    if (s() && *next == '\0') {
        std::cout << "match";
    }
    else
        std::cout << "no match";
}

2

这是功能而不是错误

仔细查看何时及何处发生回溯:

     1.           2.          3.          4.          5.          6.          7.          8.          9.          10.         11.         12.

     S            S           S           S           S           S           S           S           S           S           S           S      
   / | \        / | \       / | \       / | \       / | \       / | \       / | \       / | \       / | \       / | \       / | \       / | \
  a  S  a      a  S  a     a  S  a     a  S  a     a  S  a     a  S  a     a  S  a     a  S  a     a  S  a     a  S  a     a  S  a     a  S  a
                / | \       / | \       / | \       / | \       / | \       / | \       / | \       / | \       / | \       / | \       /   \
               a  S  a     a  S  a     a  S  a     a  S  a     a  S  a     a  S  a     a  S  a     a  S  a     a  S  a     a  S  a     a     a
                            / | \       / | \       / | \       / | \       / | \       / | \       / | \       / | \       / | \
                           a  S  a     a  S  a     a  S  a     a  S  a     a  S  a     a  S  a     a  S  a     a  S  a     a  S  a
                                        / | \       / | \       / | \       / | \       / | \       / | \       / | \       /   \
                                       a  S  a     a  S  a     a  S  a     a  S  a     a  S  a     a  S  a     a  S  a     a     a
                                                    / | \       / | \       / | \       / | \       / | \       /   \
                                                   a  S  a     a  S  a     a  S  a     a  S  a     a  S  a     a     a
                                                                / | \       / | \       / | \       /   \   
                                                               a  S  a     a  S  a     a  S  a     a     a
                                                                            / | \       /   \
                                                                           a  S  a     a     a



w[] = 'aaaaaa'  //input
l[] = ''        //current tree leafs


 1. tree:   The parser starts with the start symbol S and tries first alternative S->aSa:       Result: w[0]  = l[0]     w = aaaaaa    l = aSa
 |          -- S->aSa works                                                                         | |     | | 
 6. tree:   The parser matches a after a:                                                       Result: w[6]  = l[6]     w = aaaaaa    l = aaaaaaSaaaaaa
 7. tree:   The parser tries S->aSa again but there is no match!                                Result: w[7] != l[7]     w = aaaaaa    l = aaaaaaaSaaaaaaa 
 8. tree:   The parser tries S->aa but there is still no match!                                 Result: w[7] != l[7]     w = aaaaaa    l = aaaaaaaaaaaaaa
 9. tree:   Backtracking after the last symbol that matched => Backtracking at l[7]             Result: w[7] != l[7]     w = aaaaaa    l = aaaaaaaaaaaa
10. tree:   Backtracking after the last symbol that matched => Backtracking at l[7]             Result: w[7] != l[7]     w = aaaaaa    l = aaaaaaaaaa
11. tree:   Backtracking after the last symbol that matched => Backtracking at l[7]             Result: w[7] != l[7]     w = aaaaaa    l = aaaaaaaa
12. tree:   Backtracking after the last symbol that matched => Backtracking at l[7]             Result: w[7] != l[7]     w = aaaaaa    l = aaaa

这里的关键点是解析器在找到最后一个匹配字符的位置之后回溯。 这就是为什么它通过在l [7]处使用S-> aal = aaaaaaaaa的树11“跳” 到l = aaaa的第十二棵树。


终于有时间编辑它!;)
Sebbas '16
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.