• Nie Znaleziono Wyników

Analiza składniowa

N/A
N/A
Protected

Academic year: 2021

Share "Analiza składniowa"

Copied!
3
0
0

Pełen tekst

(1)

Analiza składniowa

Marcin Orchel

1 Wstęp

Analizator składniowy, czyli parser pobiera ciąg tokenów z analizatora leksykalnego i weryfikuje czy ten ciąg nazw tokenów może zostać wygenerowany zgodnie z gramatyką języka źródłowego. Celem jest zgłoszenie błędów składniowych i samodzielna naprawa niektórych z nich. Parser konstruuje drzewa rozbioru. Wyróżniamy metody zstępujące i wstępujące. Metody zstępujące budują drzewa rozbioru od korzenia w dół do liści, metody wstępujące (bottom-up) zaczynają od liści i budują drzewa wyprowadzenia w górę do korzenia. Strategie przywracania kontroli po błędzie to tryb paniki, przywracanie na poziomie frazy, reguły produkcji dla błędów, korekcja globalna (liczba wstawień, usunięć i zmian tokenów potrzebnych do przekształcenia x w y jest tak mała jak to możliwe, korekta najmniejszego kosztu).

Analiza leksykalna vs składniowa. Wyrażenia regularne są najbardziej użyteczne przy opisie struktur takich konstrukcji, jak identyfikatory, stałe, słowa kluczowe, odstępy.

Gramatyki są najbardziej przydatne przy opisywaniu zagnieżdżonych struktur takich jak zrównoważone nawiasy, początki i końce bloków, odpowiadających sobie if-then-else, itp.

Struktura programu może być zapisana za pomocą gramatyki, w której terminalami są tokeny, słowami tej gramatyki są ciągi tokenów czyli poprawnie zdefiniowane progra- my. Parsowanie polega na rozpoznaniu czy dany program należy do języka zdefiniowa- nego za pomocą danej gramatyki, a więc czy jest poprawnie zdefiniowanym programem.

Na wyjściu parsowania zwracane jest również drzewo rozbioru. Podczas interpretacji programu przechodzimy przez drzewo rozbioru dla podanego programu i stosujemy ope- racje, które zostały zdefiniowane dla poszczególnych produkcji. Gramatyka dla podanego przykładu z sumowaniem może wyglądać następująco:

1. S → IDEN T SAV E_OP T

2. T → N U M BER ADD_OP N U M BER

Podczas parsowania LR najpierw stosujemy drugą produkcję, a następnie pierwszą. Do każdej produkcji przypisane są odpowiednie operacje.

1.1 Parser Yacc

Yacc używa parsera LALR. Program źródłowy Yacc składa się z sekcji declarations, translation rules i supporting C routines. W sekcji deklaracje znajdują sie deklaracje

1

(2)

tokenów. We sekcji translation rules umieszczamy reguły składające się z produkcji gra- matycznej i powiązanej z nią akcji semantycznej.

1.2 Konflikty

Wyróżniamy konflikty redukcja/redukcja oraz przesunięcie/redukcja. Konflikt redukcja- /redukcja rozwiązywany jest za pomocą wyboru wcześniejszej produkcji z pliku. Konflikt przesunięcie/redukcja rozwiązywany jest przez preferowanie przesunięcia. Dodatkowo możemy przypisać symbolom terminalnym ich priorytety i łączność, np.

%lef t

0

+

0 0

0

(1)

Dołączane są one do każdej produkcji i do każdego terminala w przypadku konfliktu.

Wyróżniamy także priorytet produkcji jako priorytet najbardziej prawego terminala. Dla konfliktu przesunięcie/redukcja, wybrana zostanie redukcja, gdy priorytet produkcji jest większy niż priorytet terminala lub gdy priorytety są równe i łączność produkcji jest lewostronna. W przeciwnym razie wybierane jest przesunięcie. Do produkcji może być dołączony znacznik wymuszający zmianę priorytetu produkcji i łączności na taką jak ma podany terminal, np.

%prec < terminal > (2)

1.3 Przywracanie kontroli po błędach

Dodajemy produkcję błędów. Dodajemy produkcję błędów A → errorα. Po wykryciu błędu parser Yacc zdejmuje symbole ze stosu, aż znajdzie najwyżej położony stan, dla którego mamy A → ·errorα. Fikcyjny token error jest przenoszony na stos. Gdy α to słowo puste, to następuje redukcja do A i wykonywana akcja semantyczna powiązana z produkcją A → errorα. Następnie parser odrzuca symbole wejściowe, aż natrafi na symbol, po którym może kontynuować normalną analizę. Jeśli α nie jest puste, to Yacc przeskakuje kolejne znaki szukając podciągu, który może zostać zredukowany do α. Jeśli α składa się tylko z terminali, to zostają przeniesione na stos po znalezieniu. Następnie parser wykona redukcję do A.

1.4 Parser Ply

Tworzymy gramatykę w notacji BNF. Do każdej produkcji przyporządkowana jest akcja semantyczna na tokenach i wartościach tokenów. Yacc używa parsowania LR. Konflikty rozwiązywane są w kierunku przesunięcia.

2 Zadania

1. Zaimplementować analizę składniową dla potęgowania, funkcji specjalnych, działań relacyjnych (mniejsze, większe, równe, różne), zmiany znaku.

2. Zaimplementować definiowanie instrukcji oddzielonych średnikiem.

2

(3)

3. Dla kilku instrukcji oddzielonych średnikami, w przypadku błędu parsowania w którejś instrukcji zaimplementować kontynuowanie parsowania kolejnych instruk- cji.

4. Zaimplementować kalkulator dla odwrotnej notacji polskiej, przykładowe wejście 4 9 +, wyjście 13, 3 7 + 3 4 5 ∗ +−, wynik −13, dla potęgowania 34ˆ, wynik 81.

5. Do kalkulatora dodać instrukcję if (if (relation) statement), while (relation) state- ment, oraz pętlę for.

2.1 Wskazówki

• Zwrócić uwagę na konflikty redukcja/redukcja.

• Zwrócić uwagę na działanie ply dla gramatyk niejednoznacznych. Przetestować precedence, łączność. Przetestować konflikty reduce/reduce/ reduce/shifting.

• Dokumentacja do PLY https://www.dabeaz.com/ply/ply.html.

• Przykładowy kalkulator https://github.com/dabeaz/ply/blob/master/example/

calc/calc.py.

• kalkulator w linuxie dla odwrotnej notacji polskiej to dc, zwykły kalkulator bc

• standard POSIX dla języka bc http://oldlinux.org/Linux.old/Ref-docs/POSIX/

all.pdf, manual dla bc https://www.gnu.org/software/bc/manual/html_mono/

bc.html

• manual do dc https://www.gnu.org/software/bc/manual/dc-1.05/html_mono/

dc.html

Literatura

3

Cytaty

Powiązane dokumenty

Drzewem Viterbiego łańcucha w nazywamy to drzewo wyprowadzenia łańcucha w, któ- re ma największe prawdopodobieństwo spośród wszystkich drzew rozkładu tego łańcucha..

Słowo oryginalne xyz miało tyle samo a i b, pokazaliśmy, że xy będzie miało tylko a, to jak zamienimy y na y 2 i wiemy, że to jest ciąg niepusty to będziemy mieli więcej a, a

2.5 Sprawdzanie czy dane słowo należy do języka gramatyki SLR(1) Sprawdzanie czy dane słowo należy do języka wygląda następująco: w każdym kroku mamy bufor wejściowy w którym

Odpowiedzi na drugiej

Języki generowane przez gramatyki tego typu noszą nazwę języków rekurencyjnie przeliczalnych.. Przez G KOMB oznaczymy klasę gramatyk kombinatorycznych, a przez L RP

PowyŜsze wyprowadzenie polegało na kaŜdorazowym zastępowaniu skrajnego lewego nieterminala prawą stroną jakiejś odpowiedniej produkcji, więc kaŜdy krok tego wyprowadzenia

 Testowanie, czy język regularny reprezentowany przez automat skończony (deterministyczny lub niedeterministyczny) jest pusty, polegające na zbadaniu, czy ze

(Wielkie litery łacińskie oznaczają symbole nieterminalne, małe litery łacińskie, cyfry oraz znaki specjalne, jak np. nawiasy okrągłe lub kwadratowe, oznaczają