Czy istnieje alternatywa dla flex/bison, która może być używana w 8-bitowych systemach wbudowanych?

Piszę mały interpreter dla prostego języka BASIC like jako ćwiczenie na mikrokontrolerze AVR w C przy użyciu łańcucha narzędzi avr-gcc. Jednak, zastanawiam się, czy istnieją jakieś narzędzia open source tam, które mogłyby pomóc mi pisanie lexer i parser.

Gdybym napisał to do uruchomienia na moim Linuksie, mógłbym użyć flex/bison. Teraz, gdy ograniczyłem się do platformy 8-bitowej, muszę to wszystko robić ręcznie, czy nie?

Author: Lundin, 2010-02-11

6 answers

Zaimplementowałem parser dla prostego języka poleceń przeznaczonego dla ATmega328p. Ten chip ma 32k ROM i tylko 2K RAM. Pamięć RAM jest zdecydowanie ważniejszym ograniczeniem-jeśli nie jesteś jeszcze przywiązany do konkretnego układu, wybierz taki z jak największą ilością pamięci RAM. To ułatwi Ci życie.

Na początku rozważałem użycie flex/bison. Zdecydowałem się na tę opcję z dwóch głównych powodów:
  • domyślnie Flex & Bison zależy od jakiegoś standardu funkcje biblioteczne (szczególnie dla We/Wy), które nie są dostępne lub nie działają tak samo w avr-libc. Jestem pewien, że istnieją obsługiwane obejścia, ale jest to dodatkowy wysiłek, który musisz wziąć pod uwagę.
  • AVR ma architekturę Harvarda . C nie jest zaprojektowany do tego, więc nawet stałe zmienne są domyślnie ładowane do pamięci RAM. Musisz użyć specjalnych makr / funkcji do przechowywania i dostępu do danych w flash i EEPROM. Flex & Bizon stwórz względnie Duże tabele wyszukiwania, które dość szybko zżrą twoją pamięć RAM. O ile się nie mylę (co jest całkiem możliwe), będziesz musiał edytować źródło wyjściowe, aby skorzystać ze specjalnych interfejsów Flash i EEPROM.

Po odrzuceniu Flex & Bison poszedłem szukać innych narzędzi generatora. Oto kilka, które "considered": {]}

Możesz też rzucić okiem na porównanie Wikipedii .

Ostatecznie skończyłem z ręcznym kodowaniem zarówno lexera, jak i parsera.

Do parsowania użyłem rekurencyjnego parsera opadania. Myślę, że Ira Baxter wykonała już odpowiednią pracę, aby omówić ten temat, a w Internecie jest mnóstwo samouczków.

Dla mojego lexera, napisałem regularne wyrażenia dla wszystkich moich terminali, wykreśliły równoważną maszynę stanów i zaimplementowały ją jako jedną gigantyczną funkcję, używając goto do przeskakiwania między Stanami. To było żmudne, ale wyniki działały świetnie. Na marginesie, goto jest świetnym narzędziem do implementacji maszyn stanowych - wszystkie Twoje stany mogą mieć wyraźne etykiety tuż obok odpowiedniego kodu, nie ma wywołania funkcji ani narzutu zmiennych stanowych, a to tak szybko,jak to możliwe. C naprawdę nie ma lepszej konstrukcji dla Budowa statycznych maszyn państwowych.

Coś do przemyślenia: lexery to tak naprawdę tylko specjalizacja parserów. Największą różnicą jest to, że zwykłe gramatyki są zwykle wystarczające do analizy leksykalnej, podczas gdy większość języków programowania ma (głównie) gramatyki bezkontekstowe. Więc nic nie stoi na przeszkodzie, aby zaimplementować lexer jako rekurencyjny Parser zejścia lub użyć generatora parserów do napisania lexera. Zwykle nie jest to tak wygodne, jak użycie bardziej wyspecjalizowanego narzędzie.

 50
Author: Steve S,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-19 14:59:17

Jeśli potrzebujesz łatwego sposobu kodowania parserów, lub jesteś ograniczony przestrzenią, powinieneś ręcznie kodować rekurencyjny Parser opadania; są to zasadniczoLL (1) parsery. Jest to szczególnie skuteczne w przypadku języków, które są tak "proste" jak podstawowe. (Zrobiłem kilka z nich w latach 70-tych!). Dobrą wiadomością jest to, że nie zawierają one żadnego kodu bibliotecznego; tylko to, co piszesz.

Są dość łatwe do zakodowania, jeśli masz już gramatykę. Najpierw trzeba pozbyć się lewych reguł rekurencyjnych (np., X = X Y). Jest to ogólnie dość łatwe do zrobienia, więc pozostawiam to jako ćwiczenie. (Nie musisz tego robić dla reguł tworzenia list; patrz dyskusja poniżej).

Wtedy jeśli masz reguła BNF postaci:

 X = A B C ;

Tworzy podprogram dla każdego elementu w regule (X, A, B, C), który zwraca wartość logiczną mówiąc: "Widziałem odpowiedni konstrukt składniowy". Dla X, kod:

subroutine X()
     if ~(A()) return false;
     if ~(B()) { error(); return false; }
     if ~(C()) { error(); return false; }
     // insert semantic action here: generate code, do the work, ....
     return true;
end X;

Podobnie dla A, B, C.

Jeśli token jest terminalem, napisz kod sprawdzający strumień wejściowy dla ciąg znaków, który składa się na terminal. Np. dla liczby sprawdź, czy strumień wejściowy zawiera cyfry i kursor strumienia wejściowego obok cyfr. Jest to szczególnie łatwe, jeśli są parsowanie z bufora (dla BASIC, masz tendencję do uzyskania jednej linii w czasie) po prostu postępuj lub nie postępuj wskaźnik skanowania bufora. Ten kod jest zasadniczo częścią lexer parsera.

Jeśli reguła BNF jest rekurencyjna... nie martw się. Wystarczy zakodować wywołanie rekurencyjne. Obsługuje reguły gramatyczne like:

T  =  '('  T  ')' ;

Można to zakodować jako:

subroutine T()
     if ~(left_paren()) return false;
     if ~(T()) { error(); return false; }
     if ~(right_paren()) { error(); return false; }
     // insert semantic action here: generate code, do the work, ....
     return true;
end T;

Jeśli masz regułę BNF z alternatywą:

 P = Q | R ;

Następnie kod P z alternatywnymi wyborami:

subroutine P()
    if ~(Q())
        {if ~(R()) return false;
         return true;
        }
    return true;
end P;

Czasami napotkasz reguły tworzenia listy. Te zwykle pozostają rekurencyjne, a sprawa jest łatwa do załatwienia. Przykład:

L  =  A |  L A ;

Możesz to zakodować jako:

subroutine L()
    if ~(A()) then return false;
    while (A()) do // loop
    return true;
end L;

Możesz w ten sposób zakodować kilkaset reguł gramatycznych w ciągu dnia lub dwóch. Jest więcej szczegółów do wypełnienia, ale podstawy tutaj powinny wystarczy.

Jeśli jesteś naprawdę Ciasno na przestrzeni, możesz zbudować maszynę Wirtualną, która implementuje te pomysły. To właśnie robiłem w latach 70-tych, kiedy 8K 16 bitowych słów było tym, co można było dostać.


Jeśli nie chcesz kodować tego ręcznie, możesz zautomatyzować go za pomocą metacompilera ( Meta II ), który produkuje zasadniczo to samo. To oszałamiająca zabawa techniczna i naprawdę wymaga całej pracy, nawet dla dużych grammars.

Sierpień 2014:

Dostaję wiele próśb o "jak zbudować AST z parserem". Po szczegóły na ten temat, który zasadniczo rozwija tę odpowiedź, zobacz moją drugą odpowiedź SO https://stackoverflow.com/a/25106688/120163

Lipiec 2015:

Jest wielu ludzi, którzy chcą napisać prosty ewaluator wyrażeń. Możesz to zrobić, wykonując te same rzeczy, co sugeruje powyższy link "AST builder"; po prostu rób arytmetykę zamiast budować węzły drzew. Oto ewaluator wyrażenia zrobiony w ten sposób .

 179
Author: Ira Baxter,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-28 13:30:02

Możesz użyć flex/bison na Linuksie z jego natywnym gcc do wygenerowania kodu, który następnie skompilujesz ze swoim gcc AVR dla osadzonego celu.

 11
Author: Paul R,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-11 16:59:58

GCC może kompilować na różnych platformach, ale uruchamiasz flex i bison na platformie, na której uruchamiasz kompilator. Po prostu wypluwają kod C, który kompilator następnie buduje. Przetestuj go, aby zobaczyć, jak duży jest wynikowy plik wykonywalny. Zauważ, że mają biblioteki czasu pracy (libfl.a itd.), że będziesz również musiał krzyżować kompilację do celu.

 2
Author: ConcernedOfTunbridgeWells,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-11 17:04:11

Spróbuj Boost:: Spirit. Jest to biblioteka tylko nagłówkowa, do której możesz wpaść i zbudować bardzo szybki, czysty parser całkowicie w C++. Przeciążone operatory w C++ są używane zamiast specjalnego pliku gramatycznego.

 -1
Author: Erik Aronesty,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-24 17:22:32

Zamiast wymyślać koło, spójrz na LUA: www.lua.org . jest to język interpretacyjny przeznaczony do osadzania w innym oprogramowaniu i używany w małych systemach, takich jak systemy wbudowane. Wbudowane drzewo parsowania składni proceduralnej, logika sterowania, matematyka i obsługa zmiennych-nie ma potrzeby wymyślania na nowo czegoś, co tysiące innych już debugowało i używało. I jest rozszerzalny, co oznacza, że możesz dodać do gramatyki, dodając własne funkcje C.

 -5
Author: Scott Hall,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-01 11:49:16