Szukasz jasnej definicji tego, czym są „tokenizer”, „parser” i „leksery” oraz w jaki sposób są one ze sobą powiązane i używane?


151

Szukam jasnej definicji tego, czym są „tokenizer”, „parser” i „lexer” i jak są one ze sobą powiązane (np. Czy parser używa tokenizera lub odwrotnie)? Muszę utworzyć program, który przejdzie przez pliki źródłowe c / h, aby wyodrębnić deklarację danych i definicje.

Szukałem przykładów i mogę znaleźć trochę informacji, ale naprawdę staram się zrozumieć podstawowe pojęcia, takie jak reguły gramatyczne, drzewa parsowania i abstrakcyjne drzewo składniowe oraz ich wzajemne powiązania. Ostatecznie koncepcje te muszą zostać zapisane w rzeczywistym programie, ale 1) jak one wyglądają, 2) czy istnieją wspólne implementacje.

Patrzyłem na Wikipedię na te tematy i programy, takie jak Lex i Yacc, ale ponieważ nigdy nie przeszedłem przez klasę kompilatora (kierunek EE), trudno mi w pełni zrozumieć, co się dzieje.

Odpowiedzi:


166

Tokenizer dzieli strumień tekstu na tokeny, zwykle szukając białych znaków (tabulatory, spacje, nowe wiersze).

Lekser jest w zasadzie tokenizerem, ale zwykle dołącza dodatkowy kontekst do tokenów - ten token jest liczbą, ten token jest literałem ciągu, ten drugi token jest operatorem równości.

Parser pobiera strumień tokenów z leksera i przekształca go w abstrakcyjne drzewo składniowe reprezentujące (zwykle) program reprezentowany przez oryginalny tekst.

Kiedy ostatnio sprawdzałem, najlepszą książką na ten temat była „Kompilatory: zasady, techniki i narzędzia”, zwykle nazywana po prostu „Smoczą księgą”.


8
Bez wątpienia „The Dragon Book” to dobra książka, ale wymaga od czytelnika dobrego uziemienia w CS. Bardziej praktyczna książka to „Writing Compilers and Interpreters” Ronalda Maka, „Modern Compiler Implementation”, Andrew Appel; „Budowa kompilatora”, Niklaus Wirth; „Kompilowanie z C # i Javą” oraz „Kompilatory i generatory kompilatorów: wprowadzenie w C ++” autorstwa Pat Terry; i oczywiście „The Definitive ANTLR Reference” Terrence'a Parra.
— Andre Artus

5
Dla pewności nie odrzucam twojej rekomendacji. „The Dragon Book” była moją pierwszą książką o technologii kompilatorów, ale była ciężka w porównaniu z, powiedzmy, książką Wirtha, którą można przeczytać w kilka godzin. Wtedy miałem kilka opcji, ponieważ była to jedyna książka, jaką mogłem dostać (był rok 1991, przed Amazon i WWW). Miałem to i kolekcję plików tekstowych wyprodukowanych przez Jacka W. Crenshawa pod tytułem „ZBUDUJEMY KOMPILER” (dzięki Jack!). Nadal jest to książka, w której można uzyskać pełniejsze zrozumienie zasad, ale większość programistów potrzebuje jedynie pragmatycznego wprowadzenia.
— Andre Artus

10
Nie zgodziłbym się, że parser / z definicji / tworzy abstrakcyjne drzewo składni. Parsery mogą generować wiele różnych wyników. Na przykład często zdarza się, że parser tworzy sekwencję wywołań do interfejsu konstruktora - zobacz wzorzec konstruktora w książce Gang of Four patterns. Kluczową kwestią jest to, że parser analizuje sekwencję tokenów, aby określić, czy sekwencja jest zgodna z jakąś (zwykle bezkontekstową) gramatyką i może wygenerować pewne dane wyjściowe w oparciu o strukturę gramatyczną sekwencji.
— Theodore Norvell

2
„Zbudujmy kompilator” jest tutaj: compilers.iecc.com/crenshaw . Odnalazłem link stąd: prog21.dadgum.com/30.html
— Roger Lipscombe

1
@Pithkos: jeśli to są jedyne ograniczenia, wszystko, co powiedziałeś, to to, że funkcja pobiera dane wejściowe z jednej nienazwanej (matematycznej) domeny i produkuje i wyprowadza w innej nienazwanej domenie, np. F (X) -> Y Prawie to oznacza możesz to nazwać tylko „funkcją”. Jeśli upierasz się, że domeną X jest <StreamOfCharacter, Grammar>, a domeną Y jest Drzewo z tą właściwością, że odzwierciedla kształt gramatyki, to F (X, G) -> T byłoby czymś, co nazwałbym a parser. Często curry F w odniesieniu do G, ponieważ G nie zmienia się często, więc F [G] (X) -> T jest tym, co zwykle postrzegasz jako parser.
— Ira Baxter

18

Przykład:

int x = 1;

Lekser lub tokeniser podzieli to na tokeny „int”, „x”, „=”, „1”, „;”.

Parser weźmie te tokeny i użyje ich do zrozumienia w jakiś sposób:

  • mamy oświadczenie
  • jest to definicja liczby całkowitej
  • liczba całkowita nazywa się „x”
  • „x” należy zainicjować wartością 1

9
Lekser zauważy, że „int”, „=” i „;” to tokeny bez dalszego znaczenia, że ​​„x” to nazwa identyfikatora lub coś w tym stylu, wartość „x”, a „1” to liczba całkowita lub liczba, wartość „1”. Tokenizer niekoniecznie to zrobi.
— David Thornley

5

Powiedziałbym, że lekser i tokenizer to w zasadzie to samo i rozbijają tekst na części składowe („tokeny”). Następnie parser interpretuje tokeny za pomocą gramatyki.

Nie przejmowałbym się jednak zbytnio precyzyjnym użyciem terminologii - ludzie często używają „parsowania” do opisania jakiejkolwiek czynności polegającej na interpretacji fragmentu tekstu.


1
W przypadku parserów PEG różnica między tokenizatorem a parserem jest jeszcze mniej jasna.
— Andre Artus

0

( dodawanie do podanych odpowiedzi )

  • Tokenizer usunie również wszelkie komentarze i zwróci tokeny tylko do Lexera.
  • Lexer zdefiniuje również zakresy dla tych tokenów (zmiennych / funkcji)
  • Parser następnie zbuduje strukturę kodu / programu

1
Witaj @downvoter, czy możesz wyjaśnić, dlaczego tak naprawdę zagłosowałeś przeciw?
— Koray Tugay

1
Nie jestem przeciwnikiem, ale myślę, że głos przeciwny mógł wynikać z tego, że twoja odpowiedź nie wydaje się poprawna. Tokenizer może usuwać szum (zazwyczaj spacje, ale może także komentarze), ale często nie zasila leksera. Lekser oparty na DFA będzie tokenizować i identyfikować, jakie są tokeny (np. Liczba, ciąg znaków, identyfikator, ale także odstępy lub komentarz), ale nie może ich określać, ponieważ wymagałoby to drzewa składni, które jest później budowane przez parser.
— Lucero

1) Nie rozumiem twojej wyraźnej różnicy między „lexer” a „tokenizer”. Zbudowałem parsery dla ponad 50 języków i nigdy nie miałem dwóch oddzielnych mechanizmów, które rozbijają tekst źródłowy na atomy, więc dla mnie to tylko synonimy. 2) Jeśli kompilujesz, usuwanie komentarzy i białych znaków ma sens w lekserze. Jeśli tworzysz narzędzia do przekształcania ze źródła do źródła, nie możesz utracić komentarzy, ponieważ muszą one ponownie pojawić się w przekształconym tekście. Dlatego ZAWSZE usuwanie komentarzy jest złe; możemy dyskutować o tym, jak udaje się zachować białe znaki. ...
— Ira Baxter

1
... [Narzędzia, które buduję (zobacz moją biografię) wychwytują oba z odpowiednią wiernością, aby odtworzyć je w przekształconym kodzie; idziemy dalej i wychwytujemy format atomów, w tym dziwne rzeczy, takie jak cudzysłowy używane w ciągach znaków i podstawa / wiodące zero licznika, a wszystko to w celu uniknięcia odrzucenia przekształconego wyniku przez użytkownika. Więc to, co przeoczyłeś, to nie tylko to, że leksykacze niekoniecznie usuwają informacje, ale w rzeczywistości mogą potrzebować przechwycić informacje wykraczające poza surowy token]. ....
— Ira Baxter

... 3) Leksery definiują „zakresy” tylko w beznadziejnie niezręcznych parserach, które mają trudności z obsługą niejednoznaczności składniowych. Parsery C i C ++ to kanoniczny przykład; zobacz moją dyskusję na stackoverflow.com/a/1004737/120163 ). Nie trzeba tego robić w ten (brzydki) sposób. Więc uważam, że twoja odpowiedź jest po prostu błędna.
— Ira Baxter
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.