You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.md
+38-22Lines changed: 38 additions & 22 deletions
Original file line number
Diff line number
Diff line change
@@ -12,20 +12,22 @@ Przeważająca większość konstruktów językowych to wyrażenia, co ma uczyni
12
12
Obejmuje to m.in.:
13
13
* bloki kodu, które zwracają wartość ostatniego wyrażenia w nich zawartego (lub `null`, jeśli są puste),
14
14
* pętle `for` i `while` zwracające po zakończeniu wartość odpowiednio licznika oraz sprawdzanego warunku,
15
-
* przypisania wartości do zmiennych.
15
+
* przypisania wartości do zmiennych (nie chodzi tu o deklaracje).
16
16
17
17
Język implementuje funkcje anonimowe i pozwala na przypisywanie ich do zmiennych.
18
18
19
-
Podstawowe typy danych będą przekazywane do funkcji przez kopię, natomiast łańcuchy znaków (których zawartość jest niezmienna) poprzez referencję.
19
+
Argumenty są przekazywane do funkcji przez kopię.
20
20
21
21
22
22
## Formalna specyfikacja i składnia
23
23
24
24
Gramatyka realizowanego języka opisana jest w pliku [gramatyka.md](docs/gramatyka.md). Reguły dotyczące operatorów są zgodne z tabelami z pliku [operatory.md](docs/operatory.md).
25
25
26
-
Nie przewiduje się na razie konfiguracji zachowania interpretera poprzez specjalne pliki.
26
+
Nie przewiduje się konfiguracji zachowania interpretera poprzez specjalne pliki.
27
27
28
-
Możliwe jest importowanie zawartości innych skryptów za pomocą instrukcji `pull`. Rolę biblioteki standardowej pełni przestrzeń nazw `std`. Zdefiniowane w "dociąganym" skrypcie elementy są wprowadzanie do przestrzeni nazw skryptu głównego, a w wypadku konfliktu nazw można odwołać się do nich z użyciem pełnej ścieżki (np. `std.io.print`).
28
+
Planowane było umożliwienie użytkownikowi importowania zawartości innych skryptów za pomocą instrukcji `pull`. Rolę biblioteki standardowej miała pełnić przestrzeń nazw `std`. Zdefiniowane w "dociąganym" skrypcie elementy miały być wprowadzane do przestrzeni nazw skryptu głównego, a w wypadku konfliktu nazw możliwe miało być odwołanie się do nich z użyciem pełnej ścieżki (np. `std.io.print`).
29
+
30
+
Ze względu na obecny brak wsparcia (w klasie interpretera) dla instrukcji `pull`, funkcje wbudowane `print` oraz `quit` zlokalizowane są bezpośrednio w środowisku użytkownika.
29
31
30
32
## Wymaganie funkcjonalne
31
33
1. typy
@@ -42,6 +44,7 @@ Możliwe jest importowanie zawartości innych skryptów za pomocą instrukcji `p
42
44
* obsługa literałów całkowitoliczbowych w formie dziesiętnej (np. `3424`), szesnastkowej (np. `0xaf`), ósemkowej (np. `0x644`) oraz dwójkowej (`0b101011`)
43
45
* obsługa literałów zmiennoprzecinkowych z opcjonalną częścią ułamkową, ale nie całkowitą (np. `25.`, ale nie `.1234`) oraz wsparciem dla notacji naukowej bez znormalizowanej mantysy (np. `12.34e15`)
@@ -60,17 +63,19 @@ Możliwe jest importowanie zawartości innych skryptów za pomocą instrukcji `p
60
63
* instrukcja warunkowa `if`
61
64
* opcjonalne części `elif` (wiele wystąpień) oraz `else` (jedno wystąpienie)
62
65
* dla `if` oraz `elif` wymagane jest zdefiniowanie warunku w nawiasach
66
+
* warunek jest spełniony, jeśli jego wartość rzutowana do typu `bool` wynosi `true`
63
67
7. instrukcje pętli
64
68
* instrukcja pętli zakresowej `for`
65
69
* pozwala na zadeklarowanie niemutowalnego licznika (o wybranej nazwie) inkrementowanego wedle specyfikacji zakresu; deklaracja ta jest opcjonalna (pętla wykona się poprawną ilość razy bez konieczności deklaracji)
66
70
* specyfikacja zakresu umieszczona jest w nawiasach po słowie kluczowym `for`, działa analogicznie do konstrukcji `range` z języka Python: możliwe jest określenie tylko górnej granicy (np. `5`), wartości startowej i górnej granicy (np. `0:5`) lub wartości startowej, górnej granicy i kroku inkrementacji (np. `0:5:2`)
67
71
* instrukcja pętli warunkowej `while`
68
72
* "klasyczna" postać - wymaga podania w nawiasach po słowie kluczowym `while` jakiegoś warunku ewaluowanego przed każdą iteracją
73
+
* warunek jest spełniony, jeśli jego wartość rzutowana do typu `bool` wynosi `true`
69
74
* przerwanie wykonania
70
75
* słowo kluczowe `break` pozwala na bezwarunkowe przerwanie wykonania obu typów pętli
71
76
* słowo kluczowe `break_if` pozwala na warunkowe przerwanie wykonania obu typów pętli - warunek należy podać w nawiasach
72
77
8. funkcje
73
-
* defiowanie funkcji anonimowych z użyciem słowa kluczowego `functi`, po którym następuje lista parametrów i ciało funkcji (blok)
78
+
* defiowanie funkcji anonimowych z użyciem słowa kluczowego `functi`, po którym następuje lista parametrów i ciało funkcji
74
79
* funkcje anonimowe mogą być przypisane do zmiennej/stałej
75
80
* funkcje anonimowe mogą przechwytywać zmienne (mechanizm domknięć), ale nie mogą ich modyfikować
76
81
* wywołanie funkcji możliwe jest z użyciem nawiasów, w których podane są argumenty, możliwe rekursywne wywołania
@@ -79,15 +84,13 @@ Możliwe jest importowanie zawartości innych skryptów za pomocą instrukcji `p
79
84
* parametry funkcji można określić słowem kluczowym `const` - nie będzie się dało wtedy modyfikować ich wartości (domyślnie są mutowalne)
80
85
* przerwanie wykonania funkcji można wymusić z użyciem słowa kluczowe `return`, po którym następić może wyrażenie stanowiące wartość zwrotną
81
86
9. obsługa błędów
82
-
* z użyciem domyślnej implementacji: wypisywanie błędów w określonym formacie (zawierającym pozycję, oznaczenie błędu i dodatkowe dane) na standardowy strumień błędów `stderr` na każdym etapie działania aplikacji (błędy znakowe, składniowe, semantyczne, czasu uruchomienia)
83
-
* przykładowe błędy:
84
-
* znakowe - nieoczekiwany znak (w szczególności ETX)
85
-
* składniowe - nieprawidłowa instrukcja, nieoczekiwany token (w szczególności ETX), przekroczenie zakresu literału
86
-
* semantyczne - przypisanie wartości do zdefiniowanej wcześniej stałej, nieznany identyfikator, brak przypisania przy definicji stałej
87
-
* czasu uruchomienia - dzielenie przez zero, `null` podany w miejsce parametru nieopcjonalnego, przekroczenie zakresu zmiennej, brak wymaganej przestrzeni nazw
88
-
* wystąpienie błędu oznacza przerwanie działania bieżącego programu (w przypadku trybu REPL nie powinno to kończyć działania interperetera)
89
-
* w przypadku błędów składniowych należy pominąć wszelkie tokeny aż do rozpoczęcia następnej instrukcji i kontynuować sprawdzanie, by użytkownik mógł zapoznać się z możliwie pełną liczbą błędów od razu
90
-
* wygodny byłby mechanizm wyjątków z użyciem słów kluczowych `try` i `catch` z możliwością definiowania własnych klas błędów, nie jest to jednak obecnie zaplanowane (wiązałoby się z potencjalnym wprowadzeniem mechanizmu dziedziczenia, wsparcia dla OOP, itd.)
87
+
* z użyciem domyślnej implementacji: wypisywanie błędów w określonym formacie (zawierającym pozycję, oznaczenie błędu i dodatkowe dane) na standardowy strumień błędów `stderr` (lub inny wybrany) na każdym etapie działania aplikacji (błędy leksykalne, składniowe, czasu uruchomienia)
88
+
* zaimplementowane błędy:
89
+
* leksykalne - nieoczekiwany znak (w szczególności koniec strumienia), przekroczenie maksymalnej długości tokena (komentarza, łańcucha znaków lub liczby), nieprawidłowy przedrostek liczbowy, brakująca część liczby (wykładnik, po przedrostku), nieznany token
90
+
* składniowe - nieoczekiwany token (w szczególności ETX), wyrażenie lub instrukcja (w miejscu innego tokena, wyrażenia, instrukcji), przekroczenie zakresu liczby całkowitej, niepoprawne użycie gałęzi `default` w dopasowaniu wzorca (podwojenie, nieumieszczenie na ostatniej pozycji), brak wartości początkowej dla zmiennej niemutowalnej, użycie tej samej nazwy parametru więcej niż raz
91
+
* czasu uruchomienia - przypisanie wartości do zdefiniowanej wcześniej stałej, nieznany identyfikator, dzielenie przez zero, `null` podany w miejsce parametru nieopcjonalnego, ponowna inicjalizacja zmiennej, nieprawidłowe rzutowanie, użycie instrukcji `return` poza funkcją, użycie instrukcji `break` poza pętlą, wywołanie funkcji z nieprawidłową liczba argumentów, wyrażenie przyjmujące wartość `null` w definicji zakresu pętli `for`, nieprawidłowa l-wartość w przypisaniu
92
+
* wystąpienie błędu czasu uruchomienia oznacza przerwanie działania interpretera i ograniczenie się do sparsowania reszty tekstu (nie dotyczy to trybu REPL, gdzie błędy czasu uruchomienia są ignorowane)
93
+
* w przypadku niemożliwych do rozwiązania błędów składniowych pomijane są wszelkie tokeny aż do rozpoczęcia następnej instrukcji i kontynuować sprawdzanie, by użytkownik mógł zapoznać się z możliwie pełną liczbą błędów od razu
91
94
10. obsługa operacji logicznych
92
95
* typ `bool`
93
96
* obsługa literałów: `true`, `false`
@@ -99,7 +102,7 @@ Możliwe jest importowanie zawartości innych skryptów za pomocą instrukcji `p
* spełnienie predykatu (poprzez podanie nazwy jednoargumentowej funkcji)
102
-
* warunki można grupować za pomocą nawaisów oraz słów kluczowych: `and`, `or`
105
+
* warunki można grupować za pomocą nawiasów oraz słów kluczowych: `and`, `or`
103
106
12. obsługa opcjonalności
104
107
* każda zmienna może przyjąć wartość `null` (`null` jest osobnego typu)
105
108
* najprostszą operacją możliwą do przeprowadzenia na wartości opcjonalnej jest dostarczenie w wyrażeniu wartości "awaryjnej" na wypadek wystąpienia nulla za pomocą operatora binarnego `??`
@@ -108,26 +111,39 @@ Możliwe jest importowanie zawartości innych skryptów za pomocą instrukcji `p
108
111
## Wymagania niefunkcjonalne
109
112
110
113
1. Lekser i parser powinny działać na tyle szybko i sprawnie, by możliwe było wyświetlanie informacji np. o błędach składniowych na żywo w trakcie pisania programu.
111
-
2. Interpreter powinien być odporny na błędy - nie zawieszać się, nie przerywać nagle działania.
114
+
2. Interpreter w trybie REPL powinien być odporny na błędy - nie zawieszać się, nie przerywać nagle działania.
112
115
113
116
## Sposób uruchomienia
114
117
115
-
Interpreter będzie dostarczony w postaci programu konsolowego. Uruchomiony bez argumentów, będzie pobierał dane ze standardowego strumienia wejściowego. Jako argumenty podać można natomiast pliki skryptowe, które zostaną wczytane i wykonane. W takim przypadku standardowe wejście pozostanie nieczynne.
118
+
Interpreter jest dostarczony w postaci programu konsolowego. Uruchomiony bez argumentów, pobiera dane ze standardowego strumienia wejściowego. Jako argumenty podać można natomiast plik skryptowy, który zostanie wczytany i wykonany. W takim przypadku standardowe wejście pozostanie nieczynne.
119
+
120
+
Do zbudowania i uruchomienia programu wymagane jest środowisko .NET w wersji >=6.
121
+
Po wejściu do katalogu Toffee należy uruchomić polecenie `dotnet run`.
* katalog Running/Operations - operacje czasu uruchomienia (np. arytmetyczne czy rzutowanie),
135
+
* katalog Running/Functions - interfejs funkcji i funkcje natywne,
136
+
* Running/EnvironmentStack.cs - klasy związane z zarządzaniem zakresem zmiennych i samymi zmiennymi,
137
+
* SyntacticAnalysis/CommentSkippingLexer.cs - klasa opakowująca interfejs leksera w celu pominięcia komentarzy,
138
+
* Running/AstPrinter.cs - drukarz drzewa składniowego,
139
+
* klasy mapujące sekwencje znaków na tokeny, tokeny na operatory, literały, czy typy
126
140
127
141
## Testowanie
128
142
129
143
Analizator leksykalny oraz składniowy pracują na zasadzie konsumpcji kolejnych znaków (lub tokenów) i generowania rezultatu, który powinien być deterministyczny, a więc i możliwy do sprawdzenia pod kątem poprawności (np. poprzez proste porównanie sekwencji).
130
144
131
-
Komponenty systemu testowane będą jednostkowo, niezależnie dzięki wykorzystaniu w implementacji podejścia obiektowego (możliwe będzie podstawienie obiektu np. leksera z użyciem atrapy).
145
+
Komponenty systemu (obecnie skaner, lekser i parser) testowane są jednostkowo, niezależnie dzięki wykorzystaniu w implementacji podejścia obiektowego (możliwe będzie podstawienie obiektu np. leksera z użyciem atrapy).
146
+
147
+
Poza przykładami mającymi zadziałać poprawnie testowane są przypadki brzegowe, np. nagłe przerwanie strumienia wejściowego, nieprawidłowa sekwencja znaków/tokenów.
132
148
133
-
Oczywiście testowane będą, poza przykładami mającymi zadziałać poprawnie, przypadki brzegowe, np. nagłe przerwanie strumienia wejściowego, dzielenie przez zero i wszelkie błędy, zaproponowane wyżej lub nie.
149
+
W testu interpretera zastosowane są testy integracyjne, podające na wejście tekst instrukcji i oczekujące określonego wyjścia.
0 commit comments