LL ayrıştırıcı - LL parser

İçinde bilgisayar Bilimi, bir LL ayrıştırıcı (Soldan sağa, En soldan türetme) bir yukarıdan aşağı ayrıştırıcı alt kümesi için bağlamdan bağımsız diller. Girişi ayrıştırır Lsağa eft, performans Len yakın türetme cümlenin.

LL ayrıştırıcıya LL (k) kullanıyorsa ayrıştırıcı k jetonlar nın-nin ileri bakmak bir cümle ayrıştırırken. Bir dilbilgisine bir LL (k) dilbilgisi LL ise (k) ayrıştırıcı ondan oluşturulabilir. Resmi bir dil LL olarak adlandırılır (k) LL'sine sahipse dil (k) dilbilgisi. LL kümesi (k) diller LL'de uygun şekilde yer almaktadır (k+1) her biri için dil k ≥ 0.[1] Bunun bir doğal sonucu, bağlamdan bağımsız tüm dillerin LL tarafından tanınamayacağıdır (k) ayrıştırıcı.

Bir LL ayrıştırıcı, sınıfını tam olarak ayrıştırırsa LL-normal olarak adlandırılır. LL-normal diller[2][3][4] LLR gramerleri, herhangi bir k için LL (k) gramerlerinin uygun bir üst kümesidir. Her LLR dilbilgisi için, dilbilgisini doğrusal zamanda ayrıştıran bir LLR ayrıştırıcısı vardır.

İki adlandırma aykırı değer ayrıştırıcı türü LL (*) ve LL (sonlu). LL (*) / LL (sonlu) ayrıştırma stratejisini kullanıyorsa, bir ayrıştırıcı LL (*) / LL (sonlu) olarak adlandırılır. [5][6] LL (*) ve LL (sonlu) ayrıştırıcılar işlevsel olarak şuna daha çok benzer: PEG ayrıştırıcılar. LL (sonlu) ayrıştırıcı, gelişigüzel bir LL (k) dilbilgisini önden okuma ve önden okuma karşılaştırmaları miktarında en iyi şekilde ayrıştırabilir. LL (*) stratejisiyle ayrıştırılabilen gramer sınıfı, sözdizimsel ve anlamsal yüklemlerin kullanılması nedeniyle bazı bağlama duyarlı dilleri kapsar ve tanımlanmamıştır. LL (*) ayrıştırıcılarının şu şekilde daha iyi düşünüldüğü öne sürülmüştür: TDPL ayrıştırıcılar.[7]Popüler yanlış anlamanın aksine, LL (*) ayrıştırıcıları genel olarak LLR değildir ve inşaat tarafından ortalama olarak daha kötü (doğrusal zamana karşı süper doğrusal) ve en kötü durumda çok daha kötü (doğrusal zamana karşı üssel) performans göstermeleri garanti edilir.

LL gramerleri, özellikle LL (1) gramerler, bu gramerler için ayrıştırıcıların oluşturulması kolay olduğundan pratik açıdan büyük ilgi görmektedir ve birçok bilgisayar dilleri bu nedenle LL (1) olacak şekilde tasarlanmıştır.[8] LL ayrıştırıcılar tablo tabanlı ayrıştırıcılardır,[kaynak belirtilmeli ] benzer LR ayrıştırıcıları. LL gramerleri şu şekilde de ayrıştırılabilir: yinelemeli iniş ayrıştırıcıları. Waite ve Goos'a (1984) göre,[9] LL (k) gramerler Stearns ve Lewis (1969) tarafından tanıtıldı.[10]

Genel Bakış

Verilen için bağlamdan bağımsız gramer ayrıştırıcı, en soldaki türev Örnek bir gramer verildi :

için en soldaki türev dır-dir:

Genel olarak, en soldaki terminal olmayanı genişletmek için bir kural seçerken birden fazla olasılık vardır. Önceki örneğin 2. adımında, ayrıştırıcı, 2. kuralı mı yoksa 3. kuralı mı uygulayacağını seçmelidir:

Verimli olabilmesi için, ayrıştırıcının mümkün olduğunda bu seçimi geriye dönmeden belirleyici olarak yapabilmesi gerekir. Bazı gramerler için, bunu okunmamış girdiye göz atarak (okumadan) yapabilir. Örneğimizde, ayrıştırıcı bir sonraki okunmamış sembolün kullanılabilecek tek doğru kural 2'dir.

Genellikle bir ayrıştırıcı ileriye bakabilir semboller. Bununla birlikte, bir dilbilgisi verildiğinde, bir dilbilgisi olup olmadığını belirleme sorunu bazıları için ayrıştırıcı karar verilemez olduğunu kabul eden. Her biri için tarafından tanınamayan bir dil var ayrıştırıcı, ancak bir .

Aşağıdaki resmi tanımı vermek için yukarıdaki analizi kullanabiliriz:

İzin Vermek bağlamdan bağımsız bir gramer olun ve . Biz söylüyoruz dır-dir , ancak ve ancak en soldaki herhangi iki türev için:

aşağıdaki koşul tutar: dizenin öneki uzunluk dizenin önekine eşittir uzunluk ima eder .

Bu tanımda, başlangıç ​​sembolü ve herhangi bir terminal olmayan. Zaten türetilmiş girdi ve henüz okunmadı ve terminal dizileridir. Yunan harfleri , ve hem terminallerin hem de terminal olmayanların herhangi bir dizesini temsil eder (muhtemelen boş). Ön ek uzunluğu, önden okuma tampon boyutuna karşılık gelir ve tanım, bu tamponun, farklı kelimelerin herhangi iki türevini ayırt etmek için yeterli olduğunu söyler.

Ayrıştırıcı

ayrıştırıcı bir deterministik aşağı itme otomatı bir sonrakine göz atma yeteneği ile okumadan sembolleri girin. Bu gözetleme yeteneği, önden okuma tampon içeriklerinin sonlu durum uzayında depolanmasıyla taklit edilebilir, çünkü hem tampon hem de giriş alfabesi boyut olarak sonludur. Sonuç olarak, bu otomatı daha güçlü hale getirmez, ancak uygun bir soyutlamadır.

Yığın alfabesi , nerede:

  • terminal olmayanlar kümesidir;
  • özel bir giriş sonu (EOI) sembolüne sahip terminal (giriş) sembolleri kümesi .

Ayrıştırıcı yığını başlangıçta EOI'nin üzerindeki başlangıç ​​sembolünü içerir: . İşlem sırasında, ayrıştırıcı, sembolü tekrar tekrar değiştirir yığının üstünde:

  • biraz ile , Eğer ve bir kural var ;
  • ile (bazı gösterimlerde ), yani yığından çıkarılırsa . Bu durumda, bir giriş sembolü okunur ve eğer ayrıştırıcı girdiyi reddeder.

Yığından kaldırılacak son sembol EOI ise, ayrıştırma başarılıdır; otomat boş bir yığın yoluyla kabul eder.

Durumlar ve geçiş işlevi açıkça belirtilmemiştir; daha uygun bir şekilde belirtilir (oluşturulur) ayrıştırma tablosu yerine. Tablo, aşağıdaki eşleştirmeyi sağlar:

  • satır: yığının üstü sembolü
  • sütun: önden okuma arabellek içeriği
  • hücre: kural numarası veya

Ayrıştırıcı geçerli bir geçiş yapamazsa, giriş reddedilir (boş hücreler). Tabloyu daha kompakt hale getirmek için, eylem terminaller için aynı olduğundan, yalnızca terminal olmayan satırlar genellikle görüntülenir.

Somut örnek

Kurmak

Bir LL (1) ayrıştırıcısının çalışmalarını açıklamak için aşağıdaki küçük LL (1) dilbilgisini ele alacağız:

  1. S → F
  2. S → (S + F)
  3. F → a

ve aşağıdaki girişi ayrıştırın:

(a + a)

Bir dilbilgisi için LL (1) ayrıştırma tablosunda, terminal olmayanların her biri için bir satır ve her terminal için bir sütun (burada şu şekilde gösterilen özel terminal dahil) bulunur. $, giriş akışının sonunu belirtmek için kullanılır).

Tablonun her bir hücresi dilbilgisinin en fazla bir kuralına işaret edebilir (numarasıyla tanımlanır). Örneğin, yukarıdaki dilbilgisi için ayrıştırma tablosunda, terminal olmayan 'S' ve terminal '(' 2 numaralı kurala işaret eder:

()a+$
S2-1--
F--3--

Bir ayrıştırma tablosu oluşturma algoritması daha sonraki bir bölümde açıklanmıştır, ancak önce ayrıştırıcının girdisini işlemek için ayrıştırma tablosunu nasıl kullandığını görelim.

Ayrıştırma prosedürü

Her adımda ayrıştırıcı, giriş akışından bir sonraki kullanılabilir sembolü ve yığından en üstteki sembolü okur. Giriş sembolü ve yığın üstü sembolü eşleşirse, ayrıştırıcı her ikisini de atarak giriş akışında ve yığında yalnızca eşleşmemiş sembolleri bırakır.

Böylelikle, ayrıştırıcı ilk adımında girdi sembolünü okur '('ve yığın üst simgesi' S '. Ayrıştırma tablosu talimatı, giriş sembolünün bulunduğu sütundan gelir '('ve yığın üst simgesi' S 'ile başlayan satır; bu hücre, ayrıştırıcıya kuralı (2) uygulaması talimatını veren '2'yi içerir. Ayrıştırıcının "S" yi "öğesine yeniden yazması gerekir( S + F )Yığından 'S' yi kaldırarak ve yığının üzerine ')', 'F', '+', 'S', '(' 'iterek yığının üzerine koyun ve bu, 2 numaralı kuralı çıktıya yazar. şu hale gelir:

[ (, S, +, F, ), $ ]

İkinci adımda, ayrıştırıcı '('giriş akışından ve yığınından, çünkü artık eşleşiyorlar. Yığın artık şu hale gelir:

[S, +, F, ), $ ]

Şimdi ayrıştırıcının bir 'a ' giriş akışında ve yığının tepesinde bir 'S'. Ayrıştırma tablosu, dilbilgisinden kuralı (1) uygulaması ve çıktı akışına 1 numaralı kuralı yazması talimatını verir. Yığın şu hale gelir:

[F, +, F, ), $ ]

Ayrıştırıcı artık bir 'a ' giriş akışında ve yığının tepesinde bir 'F'. Ayrıştırma tablosu, dilbilgisinden kuralı (3) uygulamasını ve çıktı akışına 3 numaralı kuralı yazmasını söyler. Yığın şu hale gelir:

[ a, +, F, ), $ ]

Ayrıştırıcı artık bir 'a ' giriş akışında ve bir 'a' yığının tepesinde. Aynı olduklarından, onu giriş akışından kaldırır ve yığının en üstünden çıkarır. Ayrıştırıcı daha sonra bir '+' giriş akışında ve '+' "a" ile olduğu gibi yığının en üstündedir, yığından çıkar ve giriş akışından kaldırılır. Bunun sonucu:

[F, ), $ ]

Sonraki üç adımda ayrıştırıcı, 'F ' Yığın üzerinde 'a ', 3 numaralı kuralı çıktı akışına yazın ve 'a ' ve ')' hem yığından hem de giriş akışından. Ayrıştırıcı böylece 'ile biter$' hem yığınında hem de giriş akışında.

Bu durumda ayrıştırıcı, girdi dizesini kabul ettiğini bildirecek ve aşağıdaki kural numaraları listesini çıktı akışına yazacaktır:

[ 2, 1, 3, 3 ]

Bu gerçekten bir kural listesidir. en soldaki türev Giriş dizesinin:

S → ( S + F )( F + F )(a + F )(a + a)

C ++ 'da ayrıştırıcı uygulaması

Aşağıda, örnek dil için tablo tabanlı bir LL ayrıştırıcısının C ++ uygulamasını izler:

#Dahil etmek <iostream>#Dahil etmek <map>#Dahil etmek <stack>Sıralama Semboller {	// semboller:	// Terminal sembolleri:	TS_L_PARENS,	// (	TS_R_PARENS,	// )	TS_A,		// bir	TS_PLUS,	// +	TS_EOS,		// $, bu durumda ' 0'a karşılık gelir	TS_INVALID,	// geçersiz simge	// Terminal olmayan semboller:	NTS_S,		// S	NTS_F		// F};/*Geçerli bir belirteci, karşılık gelen terminal sembolüne dönüştürür*/Semboller Lexer(kömür c){	değiştirmek (c)	{		durum '(':  dönüş TS_L_PARENS;		durum ')':  dönüş TS_R_PARENS;		durum 'a':  dönüş TS_A;		durum '+':  dönüş TS_PLUS;		durum '\0': dönüş TS_EOS; // yığının sonu: $ terminal sembolü		varsayılan:   dönüş TS_INVALID;	}}int ana(int argc, kömür **argv){	kullanma ad alanı std;	Eğer (argc < 2)	{		cout << "kullanım: n  t'(a + a)' "olacak << son;		dönüş 0;	}	// LL ayrıştırıcı tablosu,  eylem için çift eşler	harita< Semboller, harita<Semboller, int> > masa;	yığın<Semboller>	ss;	// sembol yığını	kömür *p;	// giriş tamponu	// sembol yığınını başlat	ss.it(TS_EOS);	// terminal, $	ss.it(NTS_S);		// terminal olmayan, S	// sembol akışı imlecini başlat	p = &argv[1][0];	// ayrıştırma tablosunu ayarlayın	masa[NTS_S][TS_L_PARENS] = 2;	masa[NTS_S][TS_A] = 1;	masa[NTS_F][TS_A] = 3;	süre (ss.boyut() > 0)	{		Eğer (Lexer(*p) == ss.üst())		{			cout << "Eşleşen semboller:" << Lexer(*p) << son;			p++;			ss.pop();		}		Başka		{			cout << "Kural " << masa[ss.üst()][Lexer(*p)] << son;			değiştirmek (masa[ss.üst()][Lexer(*p)])			{				durum 1:	// 1. S → F					ss.pop();					ss.it(NTS_F);	// F					kırmak;				durum 2:	// 2. S → (S + F)					ss.pop();					ss.it(TS_R_PARENS);	// )					ss.it(NTS_F);		// F					ss.it(TS_PLUS);	// +					ss.it(NTS_S);		// S					ss.it(TS_L_PARENS);	// (					kırmak;				durum 3:	// 3. F → a					ss.pop();					ss.it(TS_A);	// bir					kırmak;				varsayılan:					cout << "ayrıştırma tablosu varsayılan olarak ayarlandı" << son;					dönüş 0;					kırmak;			}		}	}	cout << "ayrıştırma tamamlandı" << son;	dönüş 0;}

Python'da ayrıştırıcı uygulama

# Tüm sabitler 0'dan indekslenirSÜRE = 0KURAL = 1# TerminallerT_LPAR = 0T_RPAR = 1T_A = 2T_PLUS = 3BAKMAK = 4T_INVALID = 5# Terminal OlmayanlarN_S = 0N_F = 1# Tabloyu ayrıştırmasa = [[ 1, -1, 0, -1, -1, -1],         [-1, -1, 2, -1, -1, -1]]KURALLAR = [[(KURAL, N_F)],         [(SÜRE, T_LPAR), (KURAL, N_S), (SÜRE, T_PLUS), (KURAL, N_F), (SÜRE, T_RPAR)],         [(SÜRE, T_A)]]yığın = [(SÜRE, BAKMAK), (KURAL, N_S)]def lexical_analysis(girdi dizesi):    Yazdır("Sözcüksel analiz")    jetonlar = []    için c içinde girdi dizesi:        Eğer c   == "+": jetonlar.eklemek(T_PLUS)        elif c == "(": jetonlar.eklemek(T_LPAR)        elif c == ")": jetonlar.eklemek(T_RPAR)        elif c == "a": jetonlar.eklemek(T_A)        Başka: jetonlar.eklemek(T_INVALID)    jetonlar.eklemek(BAKMAK)    Yazdır(jetonlar)    dönüş jetonlardef syntactic_analysis(jetonlar):    Yazdır("Sözdizimsel analiz")    durum = 0    süre len(yığın) > 0:        (stype, svalue) = yığın.pop()        jeton = jetonlar[durum]        Eğer stype == SÜRE:            Eğer svalue == jeton:                durum += 1                Yazdır("pop", svalue)                Eğer jeton == BAKMAK:                    Yazdır("giriş kabul edildi")            Başka:                Yazdır("girişte kötü terim:", jeton)                kırmak        elif stype == KURAL:            Yazdır("svalue", svalue, "jeton", jeton)            kural = masa[svalue][jeton]            Yazdır("kural", kural)            için r içinde ters(KURALLAR[kural]):                yığın.eklemek(r)        Yazdır("yığın", yığın)girdi dizesi = "(a + a)"syntactic_analysis(lexical_analysis(girdi dizesi))

Uyarılar

Örnekten de görülebileceği gibi, ayrıştırıcı, yığının tepesinin terminal olmayan, terminal veya özel sembol olmasına bağlı olarak üç tür adım gerçekleştirir. $:

  • Üst uç bir terminal değilse, ayrıştırıcı, bu terminale ve giriş akışındaki sembole dayanarak ayrıştırma tablosuna bakar; yığın üzerindeki terminal olmayanları değiştirmek için hangi dilbilgisi kuralını kullanması gerekir. Kuralın numarası çıktı akışına yazılır. Ayrıştırma tablosu böyle bir kural olmadığını gösteriyorsa, ayrıştırıcı bir hata bildirir ve durur.
  • Üst kısım bir uçbirim ise, ayrıştırıcı bunu giriş akımındaki sembolle karşılaştırır ve eşitlerse ikisi de kaldırılır. Eşit değillerse, ayrıştırıcı bir hata bildirir ve durur.
  • En iyisi ise $ ve giriş akışında ayrıca bir $ daha sonra ayrıştırıcı, girdiyi başarıyla ayrıştırdığını bildirir, aksi takdirde bir hata bildirir. Her iki durumda da ayrıştırıcı duracaktır.

Bu adımlar, ayrıştırıcı durana kadar tekrar edilir ve daha sonra, ya girişi tamamen ayrıştırmış ve bir en soldaki türev veya çıkış akışına bir hata bildirmiş olacaktır.

LL (1) ayrıştırma tablosu oluşturma

Ayrıştırma tablosunu doldurmak için, ayrıştırıcının terminal olmayan bir tablo görmesi durumunda hangi dilbilgisi kuralını seçmesi gerektiğini belirlemeliyiz. Bir yığınının üstünde ve bir sembol a Bu tür bir kuralın giriş akışında olması gerektiğini görmek kolaydır. Birw ve karşılık gelen dil w ile başlayan en az bir dizeye sahip olmalıdır aBu amaçla, İlk set nın-nin w, burada şu şekilde yazılmıştır Fi(w), bazı dizelerin başında bulunabilen terminaller kümesi olarak wartı ε boş dizge de aitse wKurallarla birlikte bir gramer verildi Bir1w1, ..., Birnwnhesaplayabiliriz Fi(wben) ve Fi(Birben) her kural için aşağıdaki gibidir:

  1. her birini başlat Fi(Birben) boş setle
  2. Fi ekle (wben) için Fi(Birben) her kural için Birbenwben, Fi aşağıdaki gibi tanımlanır:
    • Fi (aw ') = { a } her terminal için a
    • Fi (Aw ') = Fi(Bir) her nonterminal için Bir ε içinde değil Fi(Bir)
    • Fi (Aw ' ) = (Fi(Bir) {ε}) ∪ Fi (w ' ) her nonterminal için Bir ε ile Fi(Bir)
    • Fi (ε) = {ε}
  3. Fi ekle (wben) için Fi(Birben) her kural için Birbenwben
  4. 2. ve 3. adımları tamamlayıncaya kadar Fi setler aynı kalır.

Sonuç, aşağıdaki sistem için en az sabit nokta çözümüdür:

  • Fi(Bir) ⊇ Fi(w) her A kuralı için → w
  • Fi(a) ⊇ { a }, her terminal için a
  • Fi(w0 w1) ⊇ Fi(w0Fi(w1), tüm kelimeler için w0 ve w1
  • Fi(ε) ⊇ {ε}

U ve V kelime grupları için, kesilmiş çarpım U · V = {(uv): 1: u ∈ U, v ∈ V} ile tanımlanır ve w: 1, w kelimelerinin ilk uzunluk-1 önekini belirtir. uzunluğu 2 veya daha fazla olan veya w'nin uzunluğu 0 veya 1 ise w'nin kendisi.

Ne yazık ki, İlk kümeler ayrıştırma tablosunu hesaplamak için yeterli değil çünkü sağ taraf w Bir kuralın eninde sonunda boş dizeye yeniden yazılabilir. Bu nedenle ayrıştırıcı da kuralı kullanmalıdır. Birw eğer ε varsa Fi(w) ve giriş akışında takip edebilecek bir sembol görür Bir. Bu nedenle, ayrıca Takip seti nın-nin Bir, olarak yazılmıştır Fo(Bir) burada terminal seti olarak tanımlanır a öyle ki bir dizi sembol var αAaβ bu, başlangıç ​​sembolünden türetilebilir. Kullanırız $ giriş akışının sonunu gösteren özel bir terminal olarak ve S başlangıç ​​sembolü olarak.

Bir dilbilgisinde son olmayanlar için Follow-setlerinin hesaplanması şu şekilde yapılabilir:

  1. başlatmak Fo(S) ile { $ } ve diğerleri Fo(Birben) boş setle
  2. formun bir kuralı varsa BirjWAbenw ' , sonra
    • eğer terminal a içinde Fi(w ' ), sonra Ekle a -e Fo(Birben)
    • eğer ε varsa Fi(w ' ), sonra Ekle Fo(Birj) için Fo(Birben)
    • Eğer w ' uzunluğu 0, sonra ekle Fo(Birj) için Fo(Birben)
  3. tümüne kadar 2. adımı tekrarlayın Fo setler aynı kalır.

Bu, aşağıdaki sistem için en az sabit nokta çözümü sağlar:

  • Fo(S) ⊇ {$}
  • Fo(Bir) ⊇ Fi(wFo(B) B biçimindeki her kural için → ... A w

Şimdi, ayrıştırma tablosunun neresinde görüneceğini tam olarak tanımlayabiliriz. T[Bir, a], terminal dışı için tablodaki girişi gösterir Bir ve terminal a, sonra

T[Bir,a] kuralı içerir Birw ancak ve ancak
a içinde Fi(w) veya
ε içinde Fi(w) ve a içinde Fo(Bir).

Eşdeğer olarak: T[Bir, a] kuralı içerir Birw her biri için aFi(wFo(Bir).

Tablo, hücrelerinin her birinde en fazla bir kural içeriyorsa, ayrıştırıcı her zaman hangi kuralı kullanması gerektiğini bilecek ve bu nedenle dizeleri geriye dönmeden ayrıştırabilecektir. Tam da bu durumda dilbilgisine bir LL (1) dilbilgisi.

LL oluşturmak (k) ayrıştırma tablosu

LL (1) ayrıştırıcılarının yapısı LL'ye uyarlanabilir (k) için k > 1, aşağıdaki değişikliklerle:

  • kesilmiş çarpım U · V = {(uv): k: u ∈ U, v ∈ V} olarak tanımlanır, burada w: k,> k uzunluğundaki kelimelerin başlangıç ​​uzunluk-k önekini veya w ise w'nin kendisini gösterir uzunluğu k veya daha az olan,
  • Fo(S) = {$k}

bir girişin sonuna k uç işaretçisi eklendiğinde $, k ilerleme bağlamını tam olarak hesaba katmak için.

1990'ların ortalarına kadar, LL'nin (k) ayrıştırma (için k > 1) pratik değildi,[kaynak belirtilmeli ] çünkü ayrıştırıcı tablo üstel boyut k en kötü durumda. Bu algı, Purdue Derleyici İnşaat Araç Seti 1992 civarında, pek çok kişinin Programlama dilleri LL tarafından verimli bir şekilde ayrıştırılabilir (k) ayrıştırıcının en kötü durum davranışını tetiklemeden ayrıştırıcı. Dahası, belirli durumlarda LL ayrıştırma, sınırsız önden okuma ile bile mümkündür. Aksine, geleneksel ayrıştırıcı üreteçleri gibi yacc kullanım LALR (1) kısıtlı bir oluşturmak için ayrıştırıcı tabloları LR ayrıştırıcı sabit bir jetonlu önden bakış ile.

Çatışmalar

Giriş bölümünde açıklandığı gibi, LL (1) ayrıştırıcıları, bağlamdan bağımsız gramerlerin özel bir durumu olan LL (1) dilbilgisine sahip dilleri tanır; LL (1) ayrıştırıcıları bağlamdan bağımsız tüm dilleri tanıyamaz. LL (1) dilleri, LR (1) dillerinin uygun bir alt kümesidir ve bunlar da bağlamdan bağımsız tüm dillerin uygun bir alt kümesidir. Bağlamdan bağımsız bir dilbilgisinin LL (1) dilbilgisi olması için, bu bölümde açıkladığımız belirli çatışmaların ortaya çıkmaması gerekir.

Terminoloji[11]

İzin Vermek Bir terminal olmayan olmak. İLK(Bir) türetilen herhangi bir dizenin ilk konumunda görünebilen terminaller kümesidir (olarak tanımlanır) Bir. TAKİP ET(Bir) birleşme: (1) İLK (B) nerede B hemen ardından gelen herhangi bir terminal olmayan Bir a'nın sağ tarafında üretim kuralı ve (2) TAKİP ET (B) nerede B formun herhangi bir kuralının başıdır BWA.

LL (1) çakışmaları

İki ana tür LL (1) çakışması vardır:

İLK / İLK çatışması

Aynı terminal olmayan kesişme için İLK iki farklı dilbilgisi kuralı kümesi. LL (1) FIRST / FIRST çatışmasına bir örnek:

S -> E | E 'a'E ->' b '| ε

İLK(E) = {b, ε} ve FIRST (E a) = {b, a}, bu nedenle tablo çizildiğinde, terminal altında çakışma var b üretim kuralı S.

Özel durum: sol özyineleme

Sol özyineleme tüm alternatiflerle İLK / İLK çatışmaya neden olur.

E -> E '+' terimi | alt1 | alt2

FIRST / FOLLOW çatışması

İLK ve İZLEME bir dilbilgisi kuralı kümesi çakışır. Bir ile boş dize (ε) İLK kümede hangi alternatifin seçileceği bilinmiyor. LL (1) çakışmasına bir örnek:

S -> A 'a' 'b'A ->' a '| ε

İLK kümesi Bir şimdi {a, ε} ve TAKİP seti {a}.

LL (1) çatışmalarına yönelik çözümler

Sol faktoring

Ortak bir sol faktör "çarpanlara ayrılmıştır".

A -> X | X Y Z

olur

A -> X BB -> Y Z | ε

İLK / İLK çatışması gibi iki alternatif aynı sembolle başladığında uygulanabilir.

Yukarıdaki İLK / İLK çatışma örneğini kullanan başka bir örnek (daha karmaşık):

S -> E | E 'a'E ->' b '| ε

olur (tek bir terminal olmayanla birleşerek)

S -> 'b' | ε | 'b' 'a' | 'a'

sonra sol faktörleme yoluyla,

S -> 'b' E | EE -> 'a' | ε

ikame

Dolaylı veya İLK / İZLE çatışmalarını kaldırmak için bir kuralı başka bir kurala değiştirmek. Bunun İLK / İLK çatışmasına neden olabileceğini unutmayın.

Sol özyineleme kaldırma[12]

Genel bir yöntem için bkz. sol özyinelemeyi kaldırmak Sol özyineleme kaldırmaya basit bir örnek: Aşağıdaki üretim kuralı, E üzerinde özyineleme bırakmıştır.

E -> E '+' TE -> T

Bu kural, '+' ile ayrılmış Ts listesinden başka bir şey değildir. Normal ifadede T ('+' T) * şeklinde. Böylece kural şu ​​şekilde yeniden yazılabilirdi:

E -> T ZZ -> '+' T ZZ -> ε

Artık hiçbir sol özyineleme ve kuralların hiçbirinde çelişki yoktur.

Bununla birlikte, tüm bağlamdan bağımsız gramerlerin eşdeğer bir LL (k) -grameri yoktur, örneğin:

S -> A | BA -> 'a' A 'b' | εB -> 'a' B 'b' 'b' | ε

Bu dilbilgisi tarafından üretilen dili kabul eden herhangi bir LL (k) -gramerinin olmadığı gösterilebilir.

Ayrıca bakınız

Notlar

  1. ^ Rosenkrantz, D. J .; Stearns, R. E. (1970). "Deterministik Yukarıdan Aşağı Gramerlerin Özellikleri". Bilgi ve Kontrol. 17 (3): 226–256. doi:10.1016 / s0019-9958 (70) 90446-8.
  2. ^ Jarzabek, Stanislav; Krawczyk, Tomasz (1974). "LL-Düzenli Dilbilgisi". Instytutu Maszyn Matematycznych: 107–119.
  3. ^ Jarzabek, Stanislav; Krawczyk, Tomasz (Kasım 1975). "LL-Normal Dilbilgisi". Bilgi İşlem Mektupları. 4 (2): 31–37. doi:10.1016/0020-0190(75)90009-5.
  4. ^ David A. Poplawski (Ağustos 1977). LL-Normal Dillerin Özellikleri (Teknik rapor). Purdue Üniversitesi, Bilgisayar Bilimleri Bölümü.
  5. ^ Parr, Terence ve Fisher, Kathleen (2011). "LL (*) ANTLR ayrıştırıcı oluşturucunun temeli". ACM SIGPLAN Bildirimleri. 46 (6): 425–436. doi:10.1145/1993316.1993548.CS1 Maint: birden çok isim: yazarlar listesi (bağlantı)
  6. ^ Belcak, Peter. "En uygun LL (k) ayrıştırması için LL (sonlu) ayrıştırma stratejisi". arXiv.org. arXiv. Alındı 27 Ekim 2020.
  7. ^ Ford Bryan (2004). "İfade Dilbilgilerini Ayrıştırma: Tanıma Temelli Sözdizimsel Temel". ACM SIGPLAN Bildirimleri. doi:10.1145/982962.964011.
  8. ^ Pat Terry (2005). C # ve Java ile Derleme. Pearson Education. s. 159–164. ISBN  9780321263605.
  9. ^ William M. Waite ve Gerhard Goos (1984). Derleyici İnşaatı. Bilgisayar Bilimlerinde Metinler ve Monograflar. Heidelberg: Springer. ISBN  978-3-540-90821-0. Burada: Mezhep. 5.3.2, s. 121-127; özellikle, s. 123.
  10. ^ Richard E. Stearns ve P.M. Lewis (1969). "Özellik Gramerler ve Tablo Makineleri". Bilgi ve Kontrol. 14 (6): 524–549. doi:10.1016 / S0019-9958 (69) 90312-X.
  11. ^ "Arşivlenmiş kopya" (PDF). Arşivlendi (PDF) 2010-06-18 tarihinde orjinalinden. Alındı 2010-05-11.CS1 Maint: başlık olarak arşivlenmiş kopya (bağlantı)
  12. ^ Modern Derleyici Tasarımı, Grune, Bal, Jacobs ve Langendoen

Dış bağlantılar