Elementy leksykalne
Lekser rozpoznaje tokeny, parser składa je w konstrukcje, a budowanie AST nadaje literałom reprezentację. Są to odrębne etapy: poprawny token nie gwarantuje poprawnej konwersji wartości ani działania wygenerowanego programu. Pełne definicje znajdują się w gramatyce.
Identyfikatory i słowa kluczowe
L01 — nazwy
IDENTIFIER ma postać [a-zA-Z_][a-zA-Z0-9_]*: litery ASCII, cyfry i podkreślenie,
bez cyfry na początku. Nazwy z polskimi literami nie należą do tego wzorca.
Samotne _ jest wcześniejszym, odrębnym tokenem UNDERSCORE; nie jest zwykłą
nazwą zmiennej. _value może być identyfikatorem.
Tokeny słów kluczowych rozróżniają wielkość liter: task i Task nie są tym
samym tokenem, a Int i int nie są alternatywną pisownią tokenu INT_TYPE.
Odrębne zachowanie porównywania nazw w analizatorze opisano w typach.
L02 — dopasowanie tokenów i kontekst
Lekser wybiera najdłuższe pasujące dopasowanie; przy równej długości rozstrzyga
kolejność reguł. Dlatego słowo kluczowe nie dzieli automatycznie dłuższej nazwy,
a _ przy równym dopasowaniu otrzymuje własny token.
Dłuższe operatory, np. ==, -> i ::, są pojedynczymi tokenami.
Nie każde słowo z własnym tokenem jest zakazane we wszystkich pozycjach nazwy.
Reguła anyIdentifier dopuszcza wybrane tokeny typów i storage:
anyIdentifier
: IDENTIFIER
| INT_TYPE | FLOAT_TYPE | BOOL_TYPE | TEXT_TYPE | SYMBOL_TYPE | TIME_TYPE | DURATION_TYPE
| STORAGE | ENTITY | QUERY | FILTER | PROJECT | SORT | LIMIT | OFFSET | KEY | ASC | DESC
| MUTATION | INSERT | UPDATE | DELETE | SINGLE | MANY | SET | WHERE
;
Dotyczy to wyłącznie miejsc używających tej reguły. Deklaracja wymagająca
IDENTIFIER nie przyjmuje automatycznie całego zbioru anyIdentifier.
Nazwy rest, grpc, mqtt nie są osobnymi tokenami protokołu.
Literały
literal
: HEX_LITERAL
| INTEGER_LITERAL
| FLOAT_LITERAL
| STRING_LITERAL
| RAW_STRING
| TRIPLE_STRING
| DURATION_LITERAL
| MEMORY_UNIT_LITERAL
| TRUE
| FALSE
;
L03 — liczby
| Token | Dokładne ograniczenia leksykalne |
|---|---|
INTEGER_LITERAL |
Cyfry dziesiętne; podkreślenia tylko pomiędzy pierwszą i ostatnią cyfrą. Wzorzec dopuszcza również kilka kolejnych podkreśleń. |
HEX_LITERAL |
Prefiks 0x albo 0X, co najmniej jedna cyfra szesnastkowa; wewnętrzne podkreślenia są dopuszczone przez wzorzec. |
FLOAT_LITERAL |
Co najmniej jedna cyfra po obu stronach kropki; opcjonalny wykładnik e/E ze znakiem i cyframi. Bez podkreśleń. |
Znak minus jest osobnym operatorem: -7 nie jest jednym tokenem liczbowym.
.5, 1. i 1e3 nie są pojedynczym tokenem FLOAT_LITERAL; poprawną postacią
wykładniczą jest np. 1.0e3. Nie oznacza to, że każdy ciąg nierozpoznany jako
jeden literał musi zostać odrzucony jako cały program: może zostać podzielony
na kilka tokenów i konstrukcji.
L04 — czas i pamięć
DURATION_LITERAL to cyfry bez znaku bezpośrednio połączone z jedną z jednostek
ns, us, ms, s, m, h, d. MEMORY_UNIT_LITERAL używa jednostek
KB, MB, GB, TB, PB, z podaną wielkością liter.
Te tokeny nie przyjmują ułamka ani podkreśleń w liczbie.
5ms jest jednym tokenem; 5 ms nim nie jest. Próba całego programu z 5 ms
przeszła check, ale nie dowodzi to równoważności tych zapisów — parser mógł
przyjąć osobne wyrażenia. Jednostka nie ustanawia dokładności zegara,
planowania wątków, sposobu przydziału pamięci ani jej trwałości.
L05 — teksty
| Postać | Reguła leksykalna |
|---|---|
"tekst" lub 'tekst' |
Zwykły tekst; bez niepoprzedzonego ukośnikiem własnego separatora, ukośnika odwrotnego i surowego CR/LF. Sekwencja \ oraz kolejny znak jest dopuszczona szeroko. |
"""tekst""" |
Tekst do pierwszego kolejnego potrójnego separatora; dopuszcza nowe wiersze. |
@"tekst" |
Tekst surowy; podwójne "" jest dopuszczone wewnątrz tokenu. Wzorzec dopuszcza nowe wiersze. |
Dopuszczenie sekwencji po ukośniku nie jest listą semantycznie obsługiwanych
escape'ów. "a\q" przechodzi próbę check; nie ustanawia to przenośnego znaczenia \q.
Token Symbol istnieje jako nazwa typu, ale :ok nie należy do reguły literal.
Null również nie ma tutaj odrębnego tokenu literału.
Białe znaki, komentarze i granice
L06 — pomijane tokeny
Lekser pomija spację, tabulator, CR i LF oraz komentarze:
//do CR/LF albo końca wejścia;/*do pierwszego*/, bez reguły zagnieżdżania komentarzy blokowych.
Komentarz może oddzielać tokeny, ale nie zastępuje wymaganego wyrażenia.
Nowy wiersz nie jest samodzielnym tokenem kończącym instrukcję. Średnik jest
opcjonalny tylko tam, gdzie odpowiednia reguła ma SEMICOLON?.
Lekser nie ma w tej regule białych znaków ogólnego zbioru separatorów Unicode.
Rozpoznanie tokenu a wartość AST
L07 — obecne ograniczenia konwersji
AstBuilder.BuildLiteral zachowuje również surowy zapis, lecz buduje wartość
następującymi ścieżkami:
- liczba dziesiętna:
long.TryParse; przy niepowodzeniu wartością pozostaje tekst; - liczba szesnastkowa:
Convert.ToInt64(raw, 16); - liczba zmiennoprzecinkowa:
double.TryParsez kulturą niezmienną; - zwykły i potrójny tekst: przycinanie znaków cudzysłowu;
- tekst surowy: usuwanie początkowego
@i przycinanie cudzysłowów; - czas i pamięć: zachowanie surowego tekstu do dalszej interpretacji.
Nie ma w tych gałęziach pełnego dekodowania escape'ów, normalizacji pojedynczych apostrofów ani zamiany podwojonych cudzysłowów tekstu surowego na jeden znak. Nie należy utożsamiać leksykalnie dopuszczonego tekstu z poprawnie zdekodowaną wartością we wszystkich generatorach.
W próbach 1__000 przechodzi check, lecz bieżąca ścieżka long.TryParse nie
usuwa podkreśleń. 0xF_F pasuje do wzorca tokenu, ale kończy się błędem P0002
podczas budowania AST. To luka między lekserem a konwersją, nie zakaz
podkreśleń wynikający z gramatyki.
Sprawdzone przykłady
Pary są sprawdzane przez CLI w osobnych projektach. Potwierdzają wyniki sprawdzania źródła, nie wartości po wykonaniu programu.
Przykład poprawny
task Main() @server {
let reading_1: Float = 21.0
}
Kontrola: przykład przyjęty przez check. Nie jest to test wykonania.
Przykład błędny
task Main() @server { let 1reading: Float = 21.0 }
Powód: Nazwa zmiennej nie może zaczynać się cyfrą.
Kontrola: przykład odrzucony przez check; kody: P0001.
Pełny zakres weryfikacji przykładów.
Przykład: Samotne podkreślenie
Poprawny zapis
task Main() @server { let _value = 1 }
Kontrola: przykład przyjęty przez check. Nie jest to test wykonania.
Błędny zapis
task Main() @server { let _ = 1 }
Powód: Samotne _ jest osobnym tokenem, nie identyfikatorem zmiennej.
Kontrola: przykład odrzucony przez check; kody: P0001.
Pełny zakres weryfikacji przykładów.
Przykład: Cyfry po obu stronach kropki
Poprawny zapis
task Main() @server { let value = 0.5 }
Kontrola: przykład przyjęty przez check. Nie jest to test wykonania.
Błędny zapis
task Main() @server { let value = .5 }
Powód: FLOAT_LITERAL wymaga cyfr przed kropką i po niej.
Kontrola: przykład odrzucony przez check; kody: P0001.
Pełny zakres weryfikacji przykładów.
Przykład: Komentarze pomiędzy tokenami
Poprawny zapis
task /* opis */ Main() @server { let value = 1 // komentarz
}
Kontrola: przykład przyjęty przez check. Nie jest to test wykonania.
Błędny zapis
task /* opis */ Main() @server { let value = }
Powód: Komentarz jest pomijany, ale nie zastępuje wymaganego wyrażenia.
Kontrola: przykład odrzucony przez check; kody: P0001.
Pełny zakres weryfikacji przykładów.
Dodatkowe próby przyjęły 1.0e3, 5ms, 4MB, tekst surowy i wielowierszowy
potrójny. Odrzucone zostały .5, nazwa z polską literą i samotne _ jako nazwa.
Przyjęcie 1e3 w całym programie nie oznacza rozpoznania jednego literału float.
Jawne luki
- Spójność separatorów cyfr z konwersją liczby oraz kontrolowane błędy zakresu.
- Pełne dekodowanie zwykłych, surowych i potrójnych tekstów, escape'ów i separatorów.
- Przenośne zakresy liczb oraz reprezentacja wartości po kolejnych etapach.
- Jednoznaczne diagnostyki przypadkowo sąsiadujących wyrażeń, które autor mógł zamierzać jako jeden literał.
- Kontrakt jednostek na poziomie wykonania, niezależny od rozpoznania tokenu.
Podstawa w repozytorium
grammar/JunexisLang.g4: tokeny literałów, operatorów, identyfikatorów, białych znaków i komentarzy oraz regułyliteralianyIdentifier.compiler/JunexisLang.Compiler.Core/Parsing/AstBuilder.cs:BuildLiteral.- Przykłady tego rozdziału i próby graniczne weryfikowane rzeczywistym CLI.