Dilbilgisi için geri izlemeli, özyinelemeli iniş ayrıştırıcı


10

Can Birisi Enlighten bana o geri çark ile özyinelemeli iniş ayrıştırıcı çalışır neden yapımları ve S bir bir gramer oluşturduğu dili tanımıyor (bu sırayla) S bir S a | a a .SaSaSaaSaSa | aa

Yalnızca dilden sözcükleri ayrıştırdığı anlaşılıyor .{a2n | n1}

Üretim kuralıyla bu ABNF Ayrıştırıcı Jeneratörünü kullanarak böyle bir ayrıştırıcı oluşturdum S = "a" S "a" / "aa"ve ayrıştırıcı aaaaaaörneğin sözcüğü tanımıyor .

Ayrıştırma ağacının terminal düğümlerinin soldan birleşmesi 7 's ile başlayana kadar üretimini kullanmasını beklerim ve daha sonra ağaç görünene kadar S a a üretimini seçerek ayrıştırma ağacına giderim. bunun gibi:SaSaaSaa

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

2
Sizce neden bu kelimeyi ayrıştıramıyor?
Yuval Filmus

@Yuval, sanırım onu ​​ayrıştırmalı, bu yüzden bir şey eksik olmalıyım.
meribold

Ah, şimdi soru daha mantıklı; düzenleme için teşekkürler! Yazdıklarınız doğruysa (kontrol etmedim) jeneratör bir hataya sahip gibi görünüyor. (Ya da dilbilgisi için belirtilmemiştir; Bence dilbilgisi temel ve açık olduğu için bu mümkün değildir.
Raphael

@ Raphael, soruyu tekrar düzenledim (umarım anlamını değiştirmeden). Aslında böyle bir ayrıştırıcının kelimeyi neden tanımadığını açıklamakla görevliyim aaaaaa.
meribold

O ağacı nereden buldun? Bu ABNF ayrıştırıcı jeneratöründen fazla bir şey alamıyorum. Verdiğiniz ağaç pek mantıklı değil. Ancak dize aaaaaaayrıştırılmalı ve ayrılmamalıdır. Ancak aaaaayrıştırır. Görünüşe göre 2 güçleri hakkında haklısın. sadece aaile ayrışır S = "aa" / "a" [S] "a". Ayrıştırıcının ne yaptığını izleyebilir misiniz?
babou

Yanıtlar:


6

Bu bir cevap değil, ama ayrıştırma ağaçları normal yorumlara uymuyor.

SaSa | aaaaaaaa

Ancak ayrıştırma ağacı aşağıdaki forma sahiptir:

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

veya bu sunumu tercih ederseniz, terminaller farklı hatlarda

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

ABNF ayrıştırıcı jeneratörün işe yaramadığını kontrol ettim, ancak ne yaptığını nasıl izleyeceğimi bilmiyorum.

{a2n | n1}

Buggy ayrıştırıcısı etrafında bu kadar ayrıntılı bir siteye sahip olmak biraz şaşırtıcıdır, bu da tamamen ilgisiz bir ayrıştırma tekniği kullanır.


Bir daha baktıktan sonra:

Sanırım sorunun bir kaynağını buldum. Köşeli parantezler isteğe bağlı anlamına gelir .

Yani dilbilginiz ya S = "a" S "a" / "aa" da yazılmalıdır S = "a" [S] "a". Sonra doğru çalışıyor gibi görünüyor.

Ancak, aynı kuralın farklı formlarda iki katı olduğunda sistem kaybolur. Neden olduğundan emin değilim.

Dilbilgisini belirlemek için sözdizimsel sorunları açıklayan bir sayfa bulamadım.

Hala o arabayý düţünüyorum.


1
Ahh. Evet. Ayrıştırma ağacını yazdığımda ne düşündüğümü bilmiyorum. Sorumu düzenleyeceğim ve sizinkini yapıştıracağım
meribold

Burada çevrimiçi bir demo ile başka bir özyinelemeli iniş, geri izleme ayrıştırıcı S ::= 'a'<S>'a' | 'a''a'
buldum

aaaaaaKullanırken hala ayrışmıyor S = "a" S "a" / "aa", ancak köşeli parantezler konusunda haklı görünüyorsunuz.
meribold

Yinelemeli iniş, geri izleme ayrıştırıcısını keşfetme noktasını görmüyorum.
babou

Haklısın S = "a" S "a" / "aa"... Çok hızlı test ettim ve ayrıştırmak yerine oluştur'a tıkladım.
babou

3

s1()SaSatrues()s2()Saa

Kelimeyi aaaaaatekrar ayrıştırmayı düşünün . Bir noktada, ayrıştırma ağacı şöyle görünecektir:

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

s()trueSSaa

   S 
 / | \
a  S  a
  / \
 a   a

Bununla birlikte, bunu genel olarak yinelemeli iniş ayrıştırıcılarını geri izleme ile değil, benim uygulamamla ilgili bir sorun olarak görüyorum.

#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

Hata değil bir özellik

Geri izlemenin ne zaman ve nerede gerçekleştiğine yakından bakın:

     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

Buradaki en önemli nokta, ayrıştırıcının son eşleşen karakterin bulunduğu konumdan sonra geriye doğru gitmesidir. Bu yüzden l = aaaaaaaa ile 11 ağacından l = aaaa ile 12. Ağaca l "de S -> aa kullanarak " atlar " [7].


Sonunda onu düzenlemek için zaman var! ;)
Sebbas
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.