LL analizátor

Az oldal jelenlegi verzióját még nem ellenőrizték tapasztalt közreműködők, és jelentősen eltérhet a 2018. január 24-én felülvizsgált verziótól ; az ellenőrzések 39 szerkesztést igényelnek .

Lásd még LL(1)

Az LL elemző ( LL parser ) egy felülről lefelé haladó elemző a számítástechnikában a környezetfüggetlen nyelvtanok egy részhalmazához , amely LL nyelvtanként ismert . Azonban nem minden környezetfüggetlen nyelvtan LL nyelvtan.

Az L betűk az "LL parser" kifejezésben azt jelentik, hogy a bemeneti karakterláncot balról jobbra elemezzük, és ezzel egyidejűleg a bal szélső származékát építjük fel.

Az LL elemzőt LL(k) elemzőnek nevezzük, ha ez az elemző előretekintést használ k tokenre (token) a bemeneti folyam elemzésekor. Az LL(k) elemző által visszalépés nélkül felismerhető nyelvtant LL(k) nyelvtannak nevezzük. Az LL(k)-nyelvtanként ábrázolható nyelvet LL(k)o-nyelvnek nevezzük. Vannak LL(k+n)-nyelvek, amelyek nem LL(k)-nyelvek.

Az alábbiakban az analizátort ismertetjük, amely táblázatok felépítésén alapul; alternatíva a rekurzív leszármazási elemző, amelyet általában kézzel írnak (bár vannak kivételek, például az ANTLR elemző generátor LL(*) nyelvtanokhoz).

Az LL(1) nyelvtanok nagyon elterjedtek, mert a hozzájuk tartozó LL elemzők csak egy karakterrel előre nézik az adatfolyamot, amikor eldöntik, melyik nyelvtani szabályt alkalmazzák. A nagy k-értékű nyelvtanokon alapuló nyelveket hagyományosan nehezen felismerhetőnek tartották, bár az LL(k) nyelvtanokat tetszőleges k-val támogató elemző generátorok elterjedt használata miatt ez a megjegyzés már nem releváns.

Az LL elemzőt LL(*) értelmezőnek nevezzük, ha nincs szigorú megkötés a k-ra, és az elemző fel tudja ismerni a nyelvet, ha a tokenek valamilyen szabályos halmazhoz tartoznak (például determinisztikus véges automaták használatával ).

Ellentmondások vannak az LL nyelvtanokra épülő, úgynevezett "európai nyelvkonstrukciós iskola" és az LR nyelvtanokat előnyben részesítő "amerikai iskola" között. Az ilyen ellentmondások hátterében a tanítás hagyományai és a különböző módszerek, eszközök konkrét tankönyvekben történő leírásának részletei állnak; szintén befolyásolta N. Wirth of ETHZ , akinek kutatásai különböző módszereket írnak le az LL(1) feloldók és fordítók optimalizálására.

Általános eset

Az értelmezőt arra tervezték, hogy megoldja azt a problémát, hogy egy karakterlánc egy adott nyelvtanhoz tartozik-e, és ha igen, akkor létrehoz egy kimeneti fát.

Az elemző a következőkből áll:

A táblázat soraiban a bolti ábécé szimbólumai, az oszlopokban pedig a terminál ábécé szimbólumai találhatók.

Az elemzés megkezdésekor a verem már két karaktert tartalmaz:

[ S, $ ]

Ahol a „$” egy speciális terminál, amely a verem végét jelzi, az „S” pedig a nyelvtan axiómája. Az elemző a nyelvtani szabályok segítségével megpróbálja lecserélni a veremben lévő axiómát a bemeneti szalagon lévő karakterlánchoz hasonló karakterláncra, majd teljesen beolvassa a szalagot, kiürítve a veremet.

Példa

Táblázat elemzése

Az LL elemző működésének magyarázatához vegye figyelembe a következő nyelvtant:

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

És elemezzük az elemzést egy karakterlánc példáján:

(1+1)

A nyelvtan elemző táblázata így néz ki:

( ) egy + $
S 2  — egy - -
F  —  — 3 - -

Van egy oszlop is egy speciális terminál számára, amelyet $ jelöl, amely a bemeneti folyam végét jelzi. A cellákban lévő (félkövéren szedett) számok a fent jelzett szabályok számait jelzik.

Elemzési eljárás

Az elemző a bemeneti adatfolyam első "(" karakterét nézi, ebben a pillanatban az "S" karakter a verem tetején van, az értelmező táblában ezeknek az értékeknek a metszéspontjában van egy szabály a nyelvtanból Ebben a példában ez a 2-es szabály, amely a veremben az „S” karakter helyettesítésére utasítja az „(S + F)” karakterláncot. A verem állapota a szabály alkalmazása után:

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

A következő lépésben az elemző beolvassa a '(' karaktert a bemeneti adatfolyamból. Mivel a verem tetején van egy hasonló '(' karakter), ezt a karaktert a rendszer kiolvassa a szalagról és eltávolítja a veremből. a verem a szabály alkalmazása után a következő:

[S, +, F, ), $]

A szalagon ezután az „1” szimbólum található, az „S” verem tetején, a táblázatban a szimbólumok metszéspontjában található az 1. szabály. A szabály alkalmazása után a táblázat szerint az analizátor alkalmazza. 3. szabály. A verem állapota a szabályok alkalmazása után:

[ F, +, F, ), $ ] [ 1, +, F, ), $ ]

Ezután az elemző beolvassa az „1” és „+” jeleket a bemeneti adatfolyamból, és mivel ezek megfelelnek a verem következő két elemének, eltávolítja őket a veremből. Ennek eredményeként a verem így néz ki:

[ F, ), $ ]

A következő három lépésben a verem 'F' karaktere '1'-re változik, majd az '1' és ')' karakterek beolvasásra kerülnek a szalagról, és eltávolításra kerülnek a veremből. Ennek eredményeként a '$' szimbólum a verem tetején és a szalagon lesz, a definíció szerint ez a lánc sikeres elemzését jelenti.

Ebben az esetben az elemző jelentést küld a sikerről, és visszaadja a következtetési folyamat során alkalmazott szabályok listáját:

[2, 1, 3, 3]

Ezek a szabályok valóban a bal szélső következtetések:

S → (S + F) → (F + F) → (1 + F) → (1 + 1)

Megjegyzések

Amint a példából látható, az elemző három különböző dolgot csinál attól függően, hogy a verem teteje nem terminál, terminál vagy $ speciális karakter:

Ezeket a lépéseket addig ismételjük, amíg meg nem áll. A leállás után vagy hibaüzenetet kapunk, vagy üzenetet arról, hogy a láncot sikeresen felismertük.

LL(1) elemző tábla készítése

Az elemző tábla feltöltéséhez meg kell határozni, hogy az elemző melyik nyelvtani szabályt válassza, ha a nem terminál A a verem tetején, az a karakter pedig a bemeneti adatfolyamban van. Könnyen belátható, hogy egy ilyen szabálynak A → w alakúnak kell lennie, és a w -nek megfelelő nyelvnek legalább egy sorral kell kezdődnie . Ebből a célból definiáljuk a w első halmazát , amelyet itt Fi(w) -ként írunk le, mint azon terminálok halmazát, amelyek w bármely sorának elején találhatók , és ε, ha az üres sor is w -hez tartozik . Adott egy A 1 → w 1 , …, A n → w n szabályokkal rendelkező nyelvtan minden szabályhoz a következőképpen számítható ki Fi(w i ) és Fi(A i ) :

  1. inicializáljon minden Fi(Ai)-t egy üres készlettel
  2. Adja hozzá Fi( wi) -t Fi(Ai)-hez minden Ai → wi szabályhoz , ahol a Fi(wi) a következőképpen van definiálva:
    • Fi ( a w' ) = { a } minden a terminálhoz
    • Fi ( A w' ) = Fi(A) minden A nem-terminálra , ahol ε nincs Fi(A) -ban
    • Fi ( A w' ) = Fi(A) \ { ε } ∪ Fi( w' ) minden A nem-terminálra , ahol ε a Fi(A-ban), beleértve az Ai -> A esetet is , w' = εazaz Fi(A w' ) = Fi(A)
    • Fi (ε) = { ε }
  3. ismételje meg a 2. lépést, amint változások következnek be a Fi készletekben .

Sajnos az első készletek nem elegendőek egy elemző tábla elkészítéséhez. Ennek az az oka, hogy a szabály jobb oldala w végül az üres karakterláncba kerülhet. Így az elemzőnek is használnia kell az A → w szabályt , ha ε Fi(w) -ben van, és a bemeneti adatfolyamban egy olyan karakter, amely követheti A -t . Ezért szükség van egy Következő A halmaz létrehozására is (itt Fo(A) -ként írjuk ), amely terminálok halmazaként van definiálva úgy, hogy legyen egy αAaβ karakterlánc , amelyet a kezdőkarakterből kaphatunk. A következő halmazok kiszámítása a nyelvtan nem terminusaihoz a következőképpen végezhető el:

  1. inicializálja Fo(S) = { $ }, és az összes többi Fo(A i ) üres halmazt
  2. ha létezik A j → wA i w' alakú szabály , akkor
    • ha az a terminál a Fi(w' ) -ben van, akkor adjunk hozzá a -t Fo(Ai - hez )
    • ha ε Fi(w' ) -ben van, akkor Fo(A i ) -hez adjuk hozzá Fo(A j ) -t
    • ha w' hossza 0, akkor Fo(A i ) -hez adjuk hozzá Fo(A j ) -t
  3. ismételje meg a 2. lépést, amíg változások következnek be az Fo készletekben .

Most pontosan meghatározhatja, hogy mely szabályok legyenek az elemző táblában. Ha T[A, a] egy bejegyzést jelöl a táblázatban egy nem terminál A és egy a terminálhoz , akkor

T[A,a] akkor és csak akkor tartalmazza az A → w szabályt , ha:

  1. a hozzáadódik Fi(A) -hoz az A → w szabály átadásakor , vagy
  2. ε Fi(A) -ban van, és a -t hozzáadjuk Fo(A)-hoz az A → w szabály átadásával .

Ha a táblázat minden cellában legfeljebb egy szabályt tartalmaz, akkor az elemző egyértelműen meg tudja határozni, hogy melyik szabályt kell alkalmazni az egyes elemzési lépésekben. Ebben az esetben a nyelvtant LL(1) nyelvtannak nevezzük .

Elemző táblák készítése LL( k ) értelmezőkhöz

Az elemző tábla mérete a legrosszabb esetben k függvényében exponenciális bonyolultságú . A Compiler Construction Toolkit (PCCTS, ma ANTLR néven ismert ) 1992 körüli megjelenése után azonban kiderült, hogy a legtöbb programozási nyelv elemzőtáblájának mérete nem szokott exponenciálisan növekedni, mivel nem ez a legrosszabb ügy. Emellett bizonyos esetekben még LL( * )-analizátorokat is lehetett készíteni. Ennek ellenére azonban a hagyományos értelmező generátorok, mint például a yacc / GNU bison , LALR( 1 ) elemző táblákat használnak egy korlátozott LR elemző létrehozásához .

LL elemző generátorok

A modern elemző generátorok az LL nyelvtanokhoz, több továbbítási várakozással, a következők:

Linkek

Jegyzetek