Clem to minimalny język programowania oparty na stosach, oferujący funkcje najwyższej klasy. Twoim celem jest napisanie tłumacza języka Clem. Powinien poprawnie wykonać wszystkie przykłady zawarte w implementacji referencyjnej, która jest dostępna tutaj .
- Jak zwykle obowiązują standardowe luki .
- Najmniejszy wpis według liczby bajtów wygrywa.
Język Clem
Clem to język programowania oparty na stosie z pierwszorzędnymi funkcjami. Najlepszym sposobem na naukę Clem jest uruchomienie clem
interpretera bez argumentów. Rozpocznie się w trybie interaktywnym, umożliwiając grę z dostępnymi poleceniami. Aby uruchomić przykładowe programy, wpisz clem example.clm
gdzie example to nazwa programu. Ten krótki samouczek powinien wystarczyć, aby zacząć.
Istnieją dwie główne klasy funkcji. Funkcje atomowe i funkcje złożone. Funkcje złożone to listy złożone z innych funkcji złożonych i funkcji atomowych. Zauważ, że funkcja złożona nie może się zawierać.
Funkcje atomowe
Pierwszym typem funkcji atomowej jest stała . Stała jest po prostu liczbą całkowitą. Na przykład -10. Gdy interpreter napotyka stałą , wypycha ją na stos. Uruchom clem
teraz. Wpisz -10
polecenie. Powinieneś zobaczyć
> -10
001: (-10)
>
Wartość 001
opisuje pozycję funkcji na stosie i (-10)
jest stałą, którą właśnie wprowadziłeś. Teraz wpisz +11
po znaku zachęty. Powinieneś zobaczyć
> +11
002: (-10)
001: (11)
>
Zauważ, że (-10)
przesunął się na drugą pozycję na stosie i (11)
teraz zajmuje pierwszą. Taka jest natura stosu! Zauważysz, że -
jest to również polecenie zmniejszenia. Za każdym razem -
lub +
poprzedzając liczbę, oznaczają one znak tej liczby, a nie odpowiadające jej polecenie. Wszystkie pozostałe funkcje atomowe są komendami . Łącznie jest ich 14:
@ Rotate the top three functions on the stack
# Pop the function on top of the stack and push it twice
$ Swap the top two functions on top of the stack
% Pop the function on top of the stack and throw it away
/ Pop a compound function. Split off the first function, push what's left,
then push the first function.
. Pop two functions, concatenate them and push the result
+ Pop a function. If its a constant then increment it. Push it
- Pop a function. If its a constant then decrement it. Push it
< Get a character from STDIN and push it to the stack. Pushes -1 on EOF.
> Pop a function and print its ASCII character if its a constant
c Pop a function and print its value if its a constant
w Pop a function from the stack. Peek at the top of the stack. While it is
a non-zero constant, execute the function.
Wpisanie polecenia w wierszu spowoduje wykonanie polecenia. Wpisz #
znak zachęty (polecenie duplikatu). Powinieneś zobaczyć
> #
003: (-10)
002: (11)
001: (11)
>
Zauważ, że (11) zostało zduplikowane. Teraz wpisz %
w wierszu polecenia (polecenie upuszczenia). Powinieneś zobaczyć
> %
002: (-10)
001: (11)
>
Aby wypchnąć polecenie na stos, po prostu umieść je w nawiasie. Wpisz (-)
polecenie. Spowoduje to przesunięcie operatora zmniejszania na stos. Powinieneś zobaczyć
> (-)
003: (-10)
002: (11)
001: (-)
>
Funkcje złożone
Możesz również zawrzeć wiele funkcji atomowych w nawiasach, aby utworzyć funkcję złożoną. Po wprowadzeniu funkcji złożonej w wierszu polecenia jest ona wypychana na stos. Wpisz ($+$)
polecenie. Powinieneś zobaczyć
> ($+$)
004: (-10)
003: (11)
002: (-)
001: ($ + $)
>
Technicznie wszystko na stosie jest funkcją złożoną. Jednak niektóre funkcje złożone na stosie składają się z jednej funkcji atomowej (w takim przypadku uznamy je za funkcje atomowe ze względu na wygodę). Podczas manipulowania funkcjami złożonymi na stosie .
często przydatne jest polecenie (konkatenacja). Wpisz .
teraz. Powinieneś zobaczyć
> .
003: (-10)
002: (11)
001: (- $ + $)
>
Zauważ, że pierwsza i druga funkcja na stosie zostały połączone, a druga funkcja na stosie jest pierwsza na liście wynikowej. Aby wykonać funkcję znajdującą się na stosie (atomową lub złożoną), musimy wydać w
polecenie (while). w
Komenda pokaże pierwszą funkcję na stosie i wykonać go wielokrotnie tak długo, jak druga funkcja na stosie jest niezerowa stała. Spróbuj przewidzieć, co się stanie, jeśli piszemy w
. Teraz wpisz w
. Powinieneś zobaczyć
> w
002: (1)
001: (0)
>
Czy tego się spodziewałeś? Dwie liczby znajdujące się na szczycie stosu zostały dodane i ich suma pozostaje. Spróbujmy jeszcze raz. Najpierw upuszczamy zero i wciskamy 10, wpisując %10
. Powinieneś zobaczyć
> %10
002: (1)
001: (10)
>
Teraz napiszemy całą funkcję w jednym ujęciu, ale dodamy dodatkową %
na końcu, aby pozbyć się zera. Wpisz (-$+$)w%
polecenie. Powinieneś zobaczyć
> (-$+$)w%
001: (11)
>
(Uwaga: ten algorytm działa tylko wtedy, gdy pierwsza stała na stosie jest dodatnia).
Smyczki
Ciągi są również obecne. Są to głównie cukier składniowy, ale mogą być bardzo przydatne. Gdy interpreter napotka ciąg, wypycha każdy znak od ostatniego do pierwszego na stos. Wpisz, %
aby usunąć 11 z poprzedniego przykładu. Teraz wpisz 0 10 "Hi!"
polecenie. 0
Wstawi terminator NULL i 10
będzie wstawić znak nowej linii. Powinieneś zobaczyć
> 0 10 "Hi!"
005: (0)
004: (10)
003: (33)
002: (105)
001: (72)
>
Wpisz, (>)w
aby wydrukować znaki ze stosu, aż napotkamy terminator NULL. Powinieneś zobaczyć
> (>)w
Hi!
001: (0)
>
Wnioski
Mam nadzieję, że powinno to wystarczyć do rozpoczęcia pracy z tłumaczem. Projekt języka powinien być stosunkowo prosty. Daj mi znać, jeśli coś jest strasznie niejasne :) Kilka rzeczy pozostało celowo niejasnych: wartości muszą być podpisane i co najmniej 16 bitów, stos musi być wystarczająco duży, aby uruchomić wszystkie programy referencyjne itp. Wiele szczegółów nie zostało wyrzeźbionych tutaj, ponieważ pełna specyfikacja języka byłaby zbyt duża do opublikowania (a jeszcze jej nie napisałem: P). W razie wątpliwości naśladuj implementację referencyjną.