Pokazuję tu Kompletny prosy kompilator napisany w czystym C (bez użycia dodatkowych narzędzi. Kompilator tłumaczy prosty język na kod w C (który można następnie przekompilować kompilatorem C i uzyskać program wykonywalny). Język jest minimalny: udostępnia tylko instrukcje podstawienia i wypisywania wyników. Zmienne mają nazwy jednoliterowe, przyjmują wartości całkowite i nie trzeba ich deklarować.`
Analizator leksykalny jest ad hoc. Symbolami są słowo 'pisz', liczby, litery (jako nazwy zmiennych), operatory arytmetyczne, średnik i znak równości. Większość symboli jest jednoznakowa, tylko liczby i słowo 'pisz' są wieloznakowe. Za wyjątkiem słowa 'pisz' pierwszy znak pozwala rozstrzygnąć jaki symbol widzimy. W przypadu litery 'p' nastepny znak decyduje, jesli jest to 'i' to jedyna możliwość to słowo 'pisz', inne znaki oznaczają że 'p' to nazwa zmiennej.
Aby móc używać symboliczne nazwy dla różnych rodzajów symboli deklarujemy typ wyliczny 'rodzaj_symbolu'. Oprócz symboli stanowiących część programy mam trzy specjalne symbole: 'sym_eof' sygnalizuje koniec programu, 'sym_blad' oznacza błędny znak, 'sym_start' jest używany przez analizator syntaktyczny. Analizator leksykalny odczytyje z tablicy 'tabela_rodzaju' rodzaj aktualnie widzianego symbolu. Litera 'p' wymaga sprawdzenia (i jeśli trzeba poprawki). Dla liczb w pętli obliczamy ich wartość.
Rozpoznanie liczb i zmiennaj 'p' wymaga oglądania następnego znaku, dlatego analizator zapamietuje jeden znak "podglądu" w zmiennej 'biezacy_znak'. Normalnie zawiera ona pierszy znak za aktualnie rozpoznanym symbolem.
Analizator syntaktyczny dla wyrażen bazuje na priorytetach opertorów. Zmienne i operatory zapamiętujemy na stosie tak długo aż zobaczymy operator o niższym (lub równym) priorytecie (lub nawias zamykający czy średnik). Po rozpoznaniu elementarnego podwyrażenia (tzn. trójki argument1 operator argument2) zastępujemy je na stosie pojedynczym węzłem drzewa rozbioru (który zachowuje się dalej podobnie jak zmienne). Specjana instrukcja 'if' rozpoznaje wyrażenia w nawiasach. W momencie gdy ta instrukcja jest wykonywana wyrażenie wewnątrz nawiasów jest już zastąpione pojedynczym węzłem drzewa.
Powną wadą analizatora jest opóźnione wykrywanie błędów. Mianowicie, dla wyrażenia takiego jak 'xyz;' dopiero po zobaczeniu średnika analizator próbuje rozpoznać podwyrażenia i zauważa błąd.
Obsługa błędów jest minimalna: wypisujemy komunikat o błędzie i kończymy pracę. Zakładamy że 'malloc' się uda i nie próbujemy zwalniać pamięci (większość jest potrzebna aż do końca).
Pierwsza wersja zawiera tylko analizator wyrażeń i funkcję wypisującą wynikowe dzrzewo. Druga wersja analizuje sekwencje podstawień (każde zakończone średnikiem). Trzecia wersja zawiera funkcje rekursywnie przechodzące po dzrzewie i generujące kod stylu C (niekompletny).
Czwarta wersja generuje kompletny kod w C (dodaje nagłówek programu z makrodefinicjami i deklaracje zmiennych). Obsługuje też instrukcję 'pisz'. Przy analizie sekwencji instrukcję 'pisz' od podstawienia rozróżniamy na podstawie pierwszego symbolu. Program wejściowy 'x=5;y=7;z=(x+y)*y;pisz(z);' daje taki wynik .
wersja wielokrotnej precyzji, realizuje artmetykę przy pomocy wywołań bibliteki GMP. Różni się od poprzedniej tylko makrodefincjami dodawanymi do nagłówka generowanego programu. Ten sam program wejściowy 'x=5;y=7;z=(x+y)*y;pisz(z);' daje taki wynik . Uwaga: przy kompilacji programu wynikowego trzeba podać '-lgmp' jako że wiekszość operacji jest wykonywana przez biblitekę GMP.
wersja generująca instrukcje assemblerowe. Różni się od `sk4.c' tym że zamiast wywołań makredefinicji bezpośrednio wypisuje instrukcje assemblera. Instrukję `pisz' dalej realizuję przez wywołanie biblioteki C. Nasz program wejściowy 'x=5;y=7;z=(x+y)*y;pisz(z);' daje taki wynik . Uwaga: program wykonywaly można otrzymać przy pomocy `gcc' (uruchamia on assembler widząc rozszerzenie `.s').