2013-04-15 6 views
5

Biorę udział w kursie budowy kompilatorów, a moje obecne zadanie polega na napisaniu lexera dla języka, który implementujemy. Nie mogę wymyślić, jak spełnić wymóg, aby lexer rozpoznawał połączone żetony. Oznacza to, że żetony nie są oddzielone białymi znakami. Np .: ciąg 39if ma być rozpoznawany jako numer 39 i słowo kluczowe if. Jednocześnie lexer musi również exit(1), gdy napotka nieprawidłowe dane wejściowe.Jak sprawić, aby lex/flex rozpoznawały tokeny nie oddzielone białymi znakami?

Uproszczona wersja kodu mam:

%{ 
#include <stdio.h> 
%} 

%option main warn debug 

%% 

if  | 
then | 
else printf("keyword: %s\n", yytext); 

[[:digit:]]+ printf("number: %s\n", yytext); 

[[:alpha:]][[:alnum:]]*  printf("identifier: %s\n", yytext); 

[[:space:]]+ // skip whitespace 
[[:^space:]]+ { printf("ERROR: %s\n", yytext); exit(1); } 

%% 

Gdy ten (lub moją pełną wersję) i przekazać go wejście 39if reguła błąd jest dopasowany, a wyjście jest ERROR: 39if, gdy Chciałbym go mieć:

number: 39 
keyword: if 

(czyli tyle samo, jakbym wszedł 39 if jako wejście.)

Going by the manual, Mam przeczucie, że przyczyną jest, że reguła błędu pasuje do dłuższych możliwych danych wejściowych niż liczba i reguły słów kluczowych, a Flex woli to. Powiedział, że nie mam pojęcia, jak rozwiązać tę sytuację. Wydaje się niewykonalne, aby napisać wyraźne wyrażenie regularne, które odrzuci wszystkie dane wejściowe bez błędów i nie wiem, jak inaczej napisać regułę "catch-all" ze względu na obsługę błędów leksykonu.

UPDATE: Przypuszczam, że po prostu sprawiają, że catch-all reguła być . { exit(1); } ale chciałbym trochę ładniejszy niż wyjście debugowania „I got mylić w wierszu 1”.

+0

a) Czy uruchomiłeś uproszczoną wersję? b) co to jest źle? –

+0

@IraBaxter Przepraszam, wydaje mi się, że zapomniałem być jednoznaczny o moim przypadku testowym, gdy zagubiłem się w spekulacjach w ostatnim paragrafie. Odpowiedzi są ** a) ** tak; oraz ** b) ** zgłasza błąd lexera zamiast dwóch tokenów. (Też dodałem je do pytania.) – millimoose

+1

Ah.OK, tak, twoja reguła "^ space" będzie jadła każdą sekwencję nie-przestrzeni, a więc konsumuje "39if". Sekret: unikaj reguł, których wyrażenia nakładają się, chyba że dłuższa zasada przychodzi bezpiecznie pierwsza. W twoim przypadku użyłbym (nie jestem lex-pert) czegoś do zastąpienia:^spacja: to nie była cyfra, ani litera, ani spacja. ... –

Odpowiedz

4

Masz całkowitą rację, że powinieneś dopasować pojedynczą "dowolną" postać jako awarię. "Standardowym" sposobem uzyskania informacji o tym, gdzie w linii jest parsowanie, jest użycie opcji --bison-bridge, ale może to być trochę uciążliwe, szczególnie jeśli nie używasz bison. Istnieje kilka innych sposobów - poszukaj w instrukcji dla sposobów określić swoje własne funkcje I/O, na przykład - ale dookoła Najprostszym IMHO jest użycie warunku początkowego:

%x LEXING_ERROR 
%% 
// all your rules; the following *must* be at the end 
.     { BEGIN(LEXING_ERROR); yyless(1); } 
<LEXING_ERROR>.+ { fprintf(stderr, 
          "Invalid character '%c' found at line %d," 
          " just before '%s'\n", 
          *yytext, yylineno, yytext+1); 
        exit(1); 
        } 

Uwaga: upewnij się, że ignorujesz spacje w swoich regułach. Wzorzec .+ pasuje do dowolnej liczby, ale co najmniej jednej nieliniowej, lub innymi słowy aż do końca bieżącej linii (zmusi to flex do odczytania tak daleko, co nie powinno stanowić problemu). yyless(n) tworzy kopię zapasową wskaźnika odczytu według znaków n, więc po dopasowaniu reguły . zostanie ponownie przeskanowany znak, który spowoduje (miejmy nadzieję) komunikat o błędzie. (To nie będzie rozsądne, jeśli twoje dane wejściowe są wielobajtowe lub mają dziwne znaki kontrolne, więc możesz napisać bardziej ostrożny kod.Od tego też może nie być uzasadnione, jeśli błąd jest na końcu wiersza, więc możesz również napisać bardziej starannego regex który dostaje więcej kontekstu, a może nawet ogranicza liczbę znaków forward czytać. wiele opcji tutaj.)

Spójrz start conditions w flex instrukcji, aby uzyskać więcej informacji o %x i BEGIN

+0

Przeczytałem o warunkach początkowych, ale nie mogłem połączyć tych elementów, dzięki! – millimoose

+0

Dużo łatwiej jest po prostu zwrócić yytext [0] do parsera w. Reguła i pozwól na odzyskanie błędu przez parser. Nie są wymagane żadne stany początkowe. Eliminuje to również wszystkie reguły dotyczące pojedynczych znaków specjalnych. – EJP

+0

@EJP: OP dokładnie stwierdza, że ​​jednym z wymagań jest to, że lexer musi "wyjść (1)", gdy napotka nieprawidłowe dane wejściowe. Nic nie wskazuje na to, że istnieje w ogóle parser, z lub bez odzyskiwania po błędzie. – rici

Powiązane problemy