Zajęcia P1AM. Analizator leksykalny (Flex + Bison, Linux)
Transkrypt
Zajęcia P1AM. Analizator leksykalny (Flex + Bison, Linux)
Zajęcia P1AM. Analizator leksykalny (Flex + Bison, Linux) 1. Cel ćwiczeń Celem ćwiczenia jest stworzenie prostego analizatora leksykalnego dla wybranego języka programowania (C, Turbo Pascal, Modula, Ada). Zadaniem tworzonego analizatora leksykalnego jest: - rozpoznawania tokenów i określanie ich właściwej wartości - eliminacja komentarzy i białych znaków - wykrywanie wskazanych dyrektyw - wykrywanie błędów analizy leksykalnej 2. Środowisko Linux, Flex + Bison A) po włączeniu komputera wybieramy system operacyjny Linux (użytkownik student) B) uruchomienie edytora tekstu i konsoli roboczej C) utworzenie w oknie konsoli własnego katalogu (mkdir nazwisko) D) przejście do własnego katalogu (cd nazwisko) E) UWAGA! Po zakończeniu zajęć należy skopiować własne projekty, a następnie skasować je z twardego dysku. 3. Informacja o programach Lex/Flex oraz Yacc/Bison A) Do utworzenia programu zaleca się skorzystać z pliku Makefile, który można ściągnąć (nie kopiować z edytora) ze strony przedmiotu! Kompilacji i łączenia dokonuje się za pomocą polecenia make. Wykonywalny plik programu nosi nazwę ada lub modula i można go wywołać pisząc ./ oraz nazwę programu. Alternatywnie można wykonać poniższe polecenia, gdzie x to ada lub modula: B) Generacja kodu parsera (pliki y.tab.c oraz y.tab.h): bison –d x.y C) Generacja kodu skanera (plik lex.yy.c): flex x.l D) Kompilacja kodu parsera (plik zad): gcc x.tab.c x.c –o x E) Przekierowanie standardowych strumieni: ./x <plik_we >plik_wy 4. Zadania do wykonania (analizator leksykalny) A) Czynności wstępne a) Ściągnięcie ze strony przedmiotu i przejrzenie wskazanych plików b) Zapisanie informacji o autorze w plikach z rozszerzeniem l i y. c) Wypisywanie własnego imienia i nazwiska na początku programu (funkcja main() w pliku z rozszerzeniem y). d) Kompilacja programu z wykorzystaniem polecenia make i pliku Makefile lub za pomocą poleceń (pkt 3). e) Uruchomienie programu B) Wykrywanie tokenów a) Wykrywanie słów kluczowych występujących w programie testowym. b) Wykrywanie tokenów wieloznakowych występujących w pliku testowym (np. ‘<=’, ‘..’ ‘:=’ - zależne od użytego języka) c) Wykrywanie identyfikatorów, liczb całkowitych i zmiennoprzecinkowych. d) Wykrywanie napisów bez użycia mechanizmu stanów. e) Wykrywanie stałych znakowych (liter i symboli) bez użycia mechanizmu stanów. f) Wykrywanie tokenów jednoznakowych (operatory, interpunkcja). UWAGI do podpunktu B! - Dla KAŻDEGO wykrytego tokena należy za pomocą gotowej funkcji process_token() wyświetlić w oddzielnym wierszu w 3 kolumnach: wykryty tekst, typ tokena oraz jego wartość znakową (yytext). Wartość tokena (3 kolumna) powinna być wyświetlana wyłącznie dla identyfikatorów, stałych liczbowych, znakowych oraz łańcuchowych (por. tab.P1_1). - Akcję należy zakończyć zwróceniem typu tokena: return( process_token( .. ) ); - Typy tokenów są zdefiniowana w pliku parser.y. Tokenów jednoznakowych nie trzeba definiować, gdyż są one jednoznacznie identyfikowane przez swój kod ASCII. C) Usuwanie (ignorowanie) komentarzy i białych znaków g) Usuwanie białych znaków (spacja, tabulacja). UWAGA! musi się znaleźć w kodzie przed j)!!! h) Usuwanie komentarzy bez użycia mechanizmu stanów. D) Zaawansowane przetwarzanie z użyciem mechanizmu stanów (warunków początkowych) i)Wykrywanie łańcuchów znaków (napisów) oraz usuwanie komentarzy j)Wykrywanie błędów: nierozpoczęty komentarz (tylko modula) k) Wykrywanie błędów: niezamkniętego komentarza wielowierszowego (tylko modula) oraz napisów wraz ze wskazaniem wiersza, w którym zostały otwarte INFORMACJE do podpunktu D) • Przykładowa deklaracja stanów (ang. start conditions) w sekcji 1 %x stan1 stan2 • Dopasowanie do wyrażenia regularnego dla automatu znajdującego się w pewnym stanie (sekcja 2): <stan1>wyr_reg1 akcja1; <stan1,stan2,INITIAL>wyr_reg2 akcja2; lub <*>wyr_reg2 akcja2; • Zmiana stanu następuje po wykonaniu akcji: <stan1>wyr_reg { .. BEGIN stan2; } • Stan początkowy – INITIAL • Stan bieżący - YYSTATE (dostępny jest również w 3 sekcji): int yywrap() { if ( YYSTATE == COMMENT ) printf( "Niezamknięty komentarz" ); ... } Tabela P1_1. Pliki testowe i przykłady działania analizatora leksykalnego Plik testowy listascii.adb: Plik testowy test.mod: -- Ten program drukuje znaki i ich kody ASCII -- Kompilacja: gnatmake listascii with Ada.Text_IO; use Ada.Text_IO; with Ada.Integer_Text_IO; procedure ListAscii is FromASCII : constant Integer := 32; ToASCII : constant Integer := 127; Uc : Character := ' '; -- zmienna Fl : Float; begin New_Line; Put_Line("Kody ASCII:"); for I in Integer range FromASCII .. ToASCII loop Ada.Integer_Text_IO.Put(I); -- procedura z Ada.Integer_Text_IO Put(' '); -- to jest procedura z Ada.Text_IO widoczna z powodu ,,use'' Put(Uc); Uc := Character'Succ(Uc); -- kolejny znak New_Line; end loop; -- tylko test Fl := 1.1 * 0.1 + 1.0e-2; -- w rzeczywistych po kropce i przed zawsze cyfra end ListAscii; (*******************************************) (*Program pokazuje kody ASCII *) (*Kompilacja: *) (*m2c -all test.mod -o test *) (*Uruchomienie: *) (*./test *) (*******************************************) MODULE test; FROM InOut IMPORT Write, WriteCard, WriteString, WriteLn; CONST FromAscii = 32; ToAscii = 127; VAR i : CARDINAL; fl : REAL; BEGIN WriteString("Kody ASCII"); WriteLn; FOR i := FromAscii TO ToAscii DO WriteCard(i, 3); Write(' '); Write(CHR(i)); WriteLn END; fl := 1.1 + 1.0E-2 + 1.0E+2 + 1.0E1; (* liczby rzeczywiste *) IF (fl <= 11.11) AND (fl >= 1.111E1) THEN WriteString("Zgodnie z oczekiwaniami") ELSE WriteString("Olaboga!") END; WriteLn END test. Początek przykładowego wyjścia analizatora leksykalnego (konkretne wartości typów tokenów zależą od kolejności ich deklaracji w pliku z rozszerzeniem y) Ada Modula 2 yytext znakowo Typ tokena with Ada . Text_IO ; use Ada . Text_IO ; with Ada . Integer_Text_IO ; procedure ListAscii is FromASCII : constant Integer := 32 ; ToASCII : constant Integer := 127 ; Uc : Character := ' ' ; Fl : Float ; begin New_Line ; Put_Line ( "Kody ASCII:" ) ; for I in Integer range FromASCII .. ToASCII loop Ada . Integer_Text_IO . Put ( I ) ; Put ( ' ' ) ; Put ( Uc KW_WITH IDENT . IDENT ; KW_USE IDENT . IDENT ; KW_WITH IDENT . IDENT ; KW_PROCEDURE IDENT KW_IS IDENT : KW_CONSTANT IDENT ASSIGN INTEGER_CONST ; IDENT : KW_CONSTANT IDENT ASSIGN INTEGER_CONST ; IDENT : IDENT ASSIGN CHARACTER_CONST ; IDENT : IDENT ; KW_BEGIN IDENT ; IDENT ( STRING_CONST ) ; KW_FOR IDENT KW_IN IDENT KW_RANGE IDENT RANGE IDENT KW_LOOP IDENT . IDENT . IDENT ( IDENT ) ; IDENT ( CHARACTER_CONST ) ; IDENT ( IDENT Wartosc tokena Ada Text_IO Ada Text_IO Ada Integer_Text_IO ListAscii FromASCII Integer 32 ToASCII Integer 127 Uc Character ' ' Fl Float New_Line Put_Line "Kody ASCII:" I Integer FromASCII ToASCII Ada Integer_Text_IO Put I Put ' ' Put Uc yytext tokena znakowo Typ tokena MODULE test ; FROM InOut IMPORT Write , WriteCard , WriteString , WriteLn ; CONST FromAscii = 32 ; ToAscii = 127 ; VAR i : CARDINAL ; fl : REAL ; BEGIN WriteString ( "Kody ASCII" ASCII" ) ; WriteLn ; FOR i := FromAscii TO ToAscii DO WriteCard ( i , 3 ) ; Write ( ' ' ) ; Write ( CHR ( i ) ) ; WriteLn END ; fl := 1.1 + 1.0E-2 KW_MODULE IDENT test ; KW_FROM IDENT InOut KW_IMPORT IDENT Write , IDENT WriteCard , IDENT WriteString , IDENT WriteLn ; KW_CONST IDENT FromAscii = INTEGER_CONST 32 ; IDENT ToAscii = INTEGER_CONST 127 ; KW_VAR IDENT i : IDENT CARDINAL ; IDENT fl : IDENT REAL ; KW_BEGIN IDENT WriteString ( STRING_CONST "Kody ) ; IDENT ; KW_FOR IDENT ASSIGN IDENT KW_TO IDENT KW_DO IDENT ( IDENT , INTEGER_CONST ) ; IDENT ( STRING_CONST ) ; IDENT ( IDENT ( IDENT ) ) ; IDENT KW_END ; IDENT ASSIGN FLOAT_CONST + FLOAT_CONST Wartosc WriteLn i FromAscii ToAscii WriteCard i 3 Write ' ' Write CHR i WriteLn fl 1.1 1.0E-2 ) ; Uc := Character ' Succ ( Uc ) ; New_Line ; end loop ; Fl := 1.1 * 0.1 + 1.0e-2 ; end ListAscii ; ) ; IDENT ASSIGN IDENT ' IDENT ( IDENT ) ; IDENT ; KW_END KW_LOOP ; IDENT ASSIGN FLOAT_CONST * FLOAT_CONST + FLOAT_CONST ; KW_END IDENT ; Uc Character Succ Uc New_Line Fl 1.1 0.1 1.0e-2 ListAscii + + 1.0E+2 FLOAT_CONST + + 1.0E1 FLOAT_CONST ; ; IF KW_IF ( ( fl IDENT <= LE 11.11 FLOAT_CONST ) ) AND KW_AND ( ( fl IDENT >= GE 1.111E1 FLOAT_CONST ) ) THEN KW_THEN WriteString IDENT ( ( "Zgodnie z oczekiwanSTRING_CONST oczekiwaniami" ) ) ELSE KW_ELSE WriteString IDENT ( ( "Olaboga!" STRING_CONST ) ) END KW_END ; ; WriteLn IDENT END KW_END test IDENT . . 1.0E+2 1.0E1 fl 11.11 fl 1.111E1 WriteString "Zgodnie z WriteString "Olaboga!" WriteLn test