Parser keturunan rekursif dengan mundur untuk tata bahasa

10

Dapatkah seseorang mencerahkan saya mengapa keturunan parser rekursif dengan backtracking yang mencoba produksi dan S a a (agar) tidak mengakui bahasa yang dibentuk oleh grammar S a S a | a a .SSebuahSSebuahSSebuahSebuahSSebuahSSebuah | SebuahSebuah

Tampaknya hanya mengurai kata-kata dari bahasa .{Sebuah2n | n1}

Saya membuat parser seperti itu menggunakan ABNF Parser Generator dengan aturan produksi S = "a" S "a" / "aa"dan parser tidak mengenali kata aaaaaa, misalnya.

Saya akan berharap untuk menggunakan produksi sampai gabungan dari parse pohon node terminal dari mulai kiri dengan 7 's, dan kemudian naik pohon parse memilih produksi S a sebuah bukannya sampai terlihat pohon seperti ini:SSebuahSSebuahaSSebuahSebuah

   S 
 / | \
a  S  a
 / | \
a  S  a
  / \
 a   a
Meribold
sumber
2
Menurut Anda mengapa kata ini tidak dapat diuraikan?
Yuval Filmus
@Yuval, saya pikir itu harus menguraikannya, jadi saya harus kehilangan sesuatu.
meribold
Ah, sekarang pertanyaannya lebih masuk akal; terima kasih untuk hasil editnya! Jika apa yang Anda tulis benar (saya tidak memeriksa), generator tersebut sepertinya memiliki bug. (Atau tidak ditentukan untuk tata bahasa Anda; Saya pikir ini tidak mungkin karena tata bahasa itu dasar dan tidak ambigu.
Raphael
@Raphael, saya mengedit pertanyaan lagi (semoga tanpa mengubah artinya). Saya sebenarnya bertugas menjelaskan mengapa parser seperti itu tidak mengenali kata itu aaaaaa.
meribold
Di mana Anda mendapatkan pohon itu. Saya tidak mendapatkan banyak dari generator parser ABNF. Pohon yang Anda berikan tidak masuk akal. Tetapi string aaaaaaharus diurai dan tidak. Tapi aaaaapakah parse. Anda tampaknya benar tentang kekuatan 2. Benda itu harus disadap. hanya diurai aadengan S = "aa" / "a" [S] "a". Bisakah Anda melacak apa yang dilakukan parser?
babou

Jawaban:

6

Ini tidak banyak jawaban, tetapi pohon parse tidak sesuai dengan komentar normal.

Tata bahasa Anda harus mengurai string a a a a a a .SSebuahSSebuah | SebuahSebuahSebuahSebuahSebuahSebuahSebuahSebuah

Tetapi pohon parse memiliki bentuk sebagai berikut:

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

atau jika Anda lebih suka presentasi ini, dengan terminal pada jalur yang berbeda

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

Saya memeriksa bahwa generator parser ABNF tampaknya tidak berfungsi, tetapi saya tidak tahu bagaimana melacak apa fungsinya.

Tampaknya memang rekonsiliasi set yang bukan apa yang didefinisikan oleh tata bahasa.{Sebuah2n | n1}

Agak mengejutkan untuk memiliki situs yang rumit di sekitar parser kereta, yang selanjutnya menggunakan teknik parsing yang sama sekali tidak menarik.


Setelah melihat lebih jauh:

Saya rasa saya menemukan satu sumber masalah. Kurung kotak berarti opsional .

Jadi tata bahasa Anda harus ditulis S = "a" S "a" / "aa" atau S = "a" [S] "a". Maka tampaknya berfungsi dengan benar.

Tetapi sistem ini tampaknya hilang ketika memiliki dua kali aturan yang sama dalam bentuk yang berbeda. Saya tidak yakin mengapa.

Saya tidak menemukan halaman yang menjelaskan masalah sintaksis ini untuk menentukan tata bahasa.

Saya masih menganggap buggy itu.

babou
sumber
1
Aduh. Ya. Saya tidak tahu apa yang saya pikirkan ketika saya menulis pohon parse itu. Saya akan mengedit pertanyaan saya dan menempelkan pertanyaan Anda.
meribold
Saya memang menemukan keturunan rekursif lain, mundur generator parser dengan demo online di sini dan itu menunjukkan perilaku yang sama dengan aturan ini:S ::= 'a'<S>'a' | 'a''a'
meribold
Itu masih tidak mengurai aaaaaasaat digunakan S = "a" S "a" / "aa", tetapi Anda tampaknya benar tentang tanda kurung.
meribold
Saya tidak melihat titik mengeksplorasi keturunan rekursif, parser backtracking.
babou
Anda benar tentang S = "a" S "a" / "aa"... Saya menguji terlalu cepat, dan mengklik menghasilkan bukan parse.
babou
3

s1()SSebuahSSebuahtrues()s2()SSebuahSebuah

Pertimbangkan untuk menguraikan kata aaaaaalagi. Pada satu titik, pohon parse akan terlihat seperti ini:

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

s()trueSSSebuahSebuah

   S 
 / | \
a  S  a
  / \
 a   a

Saya cenderung menganggap ini masalah dengan implementasi saya dan tidak dengan backtracking parser keturunan rekursif pada umumnya.

#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";
}
Meribold
sumber
2

Ini adalah fitur bukan bug

Lihat dari dekat kapan & di mana terjadi backtracking:

     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

Poin penting di sini adalah bahwa parser melakukan backtracks setelah posisi, di mana karakter yang cocok terakhir ditemukan. Itu sebabnya ia "melompat" dari pohon 11 dengan l = aaaaaaaa ke pohon ke-12 dengan l = aaaa dengan menggunakan S -> aa di l [7].

Sebbas
sumber
Akhirnya ada waktu untuk mengeditnya! ;)
Sebbas