Wiązania protokołów
protocol jest osobną deklaracją najwyższego poziomu, wiążącą powierzchnię
komunikacyjną z zadaniami. Nie nadaje zadaniu platformy. Poniższy opis
oddziela składnię języka, interpretację tekstu wiązań i generowanie kodu.
Składnia
protocolDeclaration
: PROTOCOL IDENTIFIER protocolBody
;
protocolBody
: LBRACE protocolContent* RBRACE
;
protocolContent
: protocolBody
| ~(LBRACE | RBRACE)
;
B01 — nazwa i ciało
Po protocol wymagany jest identyfikator oraz ciało w klamrach. Ciało może być
puste. Parser zachowuje jego treść i dopuszcza zagnieżdżone klamry; nie definiuje
ustrukturyzowanej gramatyki metod, ścieżek ani argumentów wiązania.
Poprawne parsowanie nie oznacza, że treść zostanie zinterpretowana.
Nazwy rest, grpc, ws, mqtt, coap, amqp czy własna nazwa są składniowo
identyfikatorami. Akceptacja nazwy nie dowodzi dostępności adaptera.
B02 — obecna normalizacja nazw
DistributedTopologyLowerer sprowadza nazwę do małych liter, a następnie stosuje
heurystykę: nazwa zawierająca rest, grpc, websocket, mqtt, coap, amqp
lub kafka jest przypisywana do odpowiedniej rodziny; ws oznacza websocket.
Pozostałe nazwy pozostają bez takiej zmiany. Sprawdzenia są uporządkowane w kodzie.
Jest to zachowanie implementacji, nie pełny kontrakt rejestru protokołów ani
mechanizmu rozszerzania języka.
Rozpoznawanie wiązań
B03 — postacie HTTP
Obecny algorytm szuka w ciele dwóch postaci, za pomocą wyrażeń regularnych:
protocol rest {
GET("/items/{id}", Read(path id: Int))
}
protocol rest {
GET /items/{id} -> Read(path id: Int)
}
Rozpoznawane metody to GET, POST, PUT, PATCH, DELETE, bez rozróżniania
wielkości liter. HEAD i OPTIONS nie należą do tych wzorców.
Postać funkcyjna wymaga ścieżki w podwójnych cudzysłowach; postać ze strzałką
przechwytuje tekst ścieżki przed ->. Nazwa zadania jest dopasowywana jako
identyfikator ASCII. Argumenty są przechwytywane do pierwszego );
nie jest to parser dowolnych zagnieżdżonych wyrażeń.
Te wzorce są obecnie przeszukiwane w każdej deklaracji protocol, nie tylko
w deklaracji nazwanej rest. Rodzina zapisana w planie wynika jednak z nazwy
deklaracji; znalezienie zapisu HTTP nie zmienia jej automatycznie na rest.
B04 — sprawdzanie zadania i powtórzeń
Rozpoznane wiązanie HTTP musi wskazywać istniejące zadanie; brak deklaracji
powoduje błąd D6001. Dopasowanie nazwy zadania rozróżnia wielkość liter.
Kontrola istnienia nie sprawdza pełnej zgodności sygnatury ani platformy wykonania.
W obrębie jednej deklaracji identyczna trójka (metoda, tekst ścieżki, zadanie) jest dodawana do planu tylko raz. To usuwanie powtórzeń w planie, nie reguła ponownego dostarczania żądań. Dwa różne zadania pod tą samą metodą i ścieżką nie są tym mechanizmem rozstrzygane jako konflikt. Nie ma wspólnego zbioru powtórzeń dla wszystkich deklaracji. Oba wzorce są przeszukiwane osobno, więc kolejność w planie nie ustanawia kolejności obsługi żądań.
B05 — źródła parametrów
Z tekstu argumentów algorytm zbiera zapisy path nazwa:, query nazwa:,
body nazwa: i header nazwa:. W planie powstaje mapa nazwy parametru na źródło.
Typ po dwukropku nie jest przez ten mechanizm porównywany z typem parametru zadania.
Nie ma tu dowodu kontroli kompletności mapy, poprawności nazw, wymaganych argumentów
ani zgodności zmiennych ścieżki z sygnaturą.
B06 — pozostałe rodziny
Dla rodziny innej niż rest dodatkowe przeszukiwanie znajduje teksty postaci
Nazwa(. Jeżeli nazwa jest zadeklarowanym zadaniem, dodaje wiązanie bez metody
i ścieżki. Nieznane nazwy w tym dodatkowym przeszukiwaniu są pomijane.
Dlatego protocol grpc { Read } nie tworzy wiązania przez tę ścieżkę,
a protocol grpc { Read() } może je utworzyć. Missing() w tym przeszukiwaniu
nie daje D6001. Nie należy z tego wyprowadzać reguł wykonywania RPC, publikacji
MQTT, generowania .proto ani subskrypcji WebSocket.
Sprawdzone przykłady
Pary poniżej przechodzą rzeczywiste CLI w osobnych projektach. Potwierdzają przyjęcie albo odrzucenie źródła; nie są próbami obsługi żądań przez uruchomiony serwer.
Przykład poprawny
task Ping() @server -> Text {
return "ok"
}
protocol rest {
GET("/ping", Ping())
}
Kontrola: przykład przyjęty przez check. Nie jest to test wykonania.
Przykład błędny
protocol { GET("/ping", Ping()) }
Powód: Deklaracja protocol wymaga nazwy.
Kontrola: przykład odrzucony przez check; kody: P0001.
Pełny zakres weryfikacji przykładów.
Przykład: Rozpoznane wiązanie i nieznane zadanie
Poprawny zapis
task Read(id: Int) @server -> Int { return id }
protocol rest { GET("/items/{id}", Read(path id: Int)) }
Kontrola: przykład przyjęty przez check. Nie jest to test wykonania.
Błędny zapis
task Read(id: Int) @server -> Int { return id }
protocol rest { GET("/items", Missing()) }
Powód: Rozpoznane wiązanie HTTP wskazuje nieistniejące zadanie Missing.
Kontrola: przykład odrzucony przez check; kody: D6001.
Pełny zakres weryfikacji przykładów.
Przykład: Postać ze strzałką
Poprawny zapis
task Read(id: Int) @server -> Int { return id }
protocol rest { GET /items/{id} -> Read(path id: Int) }
Kontrola: przykład przyjęty przez check. Nie jest to test wykonania.
Błędny zapis
task Read(id: Int) @server -> Int { return id }
protocol rest { GET /items -> Missing() }
Powód: Rozpoznana postać ze strzałką również wymaga istniejącego zadania.
Kontrola: przykład odrzucony przez check; kody: D6001.
Pełny zakres weryfikacji przykładów.
Co obecnie generuje ścieżka REST w Go
GoCodeGenerator wybiera z planu wiązania rodziny rest dotyczące zadań
z adnotacją @server. Dla wpisów z metodą i ścieżką emituje rejestrację
net/http i wywołanie zadania. Parametry bierze z sygnatury zadania, nie z typów
w tekście wiązania.
Gdy mapa nie wskazuje źródła parametru, generator wybiera path, jeśli ścieżka
zawiera {nazwaParametru}, w przeciwnym razie query. Jawne body lub typ
inny niż IRNamedType prowadzą do dekodowania JSON z ciała. Pozostałe przypadki
korzystają z odczytu ścieżki, nagłówka lub zapytania i konwersji według typu.
To opis aktualnego generatora, nie uniwersalny kontrakt wszystkich adapterów.
Generator normalizuje zapis {id:Typ} do {id} i usuwa spacje ze ścieżki.
Zadanie bez typu wyjścia prowadzi do odpowiedzi 204; z typem wyjścia — do kodowania
wyniku jako JSON. Nie dowodzi to poprawnego mapowania dowolnego typu wyniku,
w szczególności typu komunikacyjnego, na odpowiedź protokołu.
Powstaje również openapi.json w wersji formatu 3.0.3, zawierający ścieżki,
metody, nazwę zadania jako operationId i ogólną odpowiedź 200. Ten generator
nie buduje pełnych schematów parametrów i wyników; odpowiedź 200 w opisie nie
odzwierciedla wszystkich gałęzi handlera.
Test Go_RestProtocol_EmitsTypedRouteAndOpenApiAsync sprawdza tekst wygenerowanej
trasy, odczyt parametru ścieżki, wywołanie zadania i obecność operationId.
Nie uruchamia serwera ani nie wysyła żądania HTTP.
Wyniki prób granicznych
Próby używały zadania Read(id: Int) @server -> Int.
| Próba | Wynik check | Granica dowodu |
|---|---|---|
| Obie opisane postacie GET do Read | przyjęte | Rozpoznawany zapis; brak dowodu obsługi żądania. |
| GET do nieistniejącego Missing | odrzucone, D6001 |
Sprawdzanie zadania w rozpoznanym wzorcu HTTP. |
Read(query absent: Text) |
przyjęte bez diagnostyk | Błędna nazwa parametru nie została wykryta. |
Read(query id: Text) |
przyjęte bez diagnostyk | Typ Text nie został porównany z Int. |
HEAD("/items", Missing()) w rest |
przyjęte bez diagnostyk | HEAD nie jest rozpoznawany; nie potwierdza poprawnego wiązania. |
protocol grpc { Missing Read } |
przyjęte bez diagnostyk | Same nazwy nie spełniają wzorca wywołania. |
protocol grpc { Missing() Read() } |
przyjęte bez diagnostyk | Dodatkowe przeszukiwanie pomija nieznane zadanie. |
protocol custom { Read() } |
przyjęte bez diagnostyk | Nie potwierdza istnienia wykonawcy custom. |
| Dwa identyczne wpisy GET | przyjęte bez diagnostyk | Brak błędu powtórzenia; algorytm planu usuwa identyczne trójki. |
protocol rest { nonsense: true } |
przyjęte bez diagnostyk | Nieznana treść nie uzyskuje przez to znaczenia. |
Jawne luki
- Ustrukturyzowana gramatyka wnętrza wiązania i diagnostyki nierozpoznanych wpisów.
- Jednolite sprawdzanie nieistniejących zadań dla wszystkich form i rodzin.
- Zgodność nazw i typów parametrów, kompletność argumentów oraz powtórzone źródła tego samego parametru. Obecna mapa nie ustanawia kontraktu takich konfliktów.
- Walidacja platformy zadania, konfliktów tras oraz spójności schematów OpenAPI z rzeczywistymi odpowiedziami i typami danych.
- Pełne kontrakty adapterów innych niż opisana ścieżka REST w Go.
- TLS, uwierzytelnianie, autoryzacja, błędy domenowe, anulowanie, retry i gwarancje
dostarczenia. Sam zapis
protocolich nie ustanawia.
Dobór transportu krawędzi pipeline jest osobnym etapem. Obecny
DistributedTopologyLowerer.LowerEdges korzysta z platform i rodzaju komunikacji,
a nie z listy jawnych wiązań protokołów. Historyczna tabela automatycznego doboru
w spec/08-protocols.md różni się od tego algorytmu; nie przedstawiamy jej jako
zweryfikowanej semantyki. Zobacz także pipeline i
rozmieszczenie.
Podstawa w repozytorium
grammar/JunexisLang.g4: trzy reguły zamieszczone powyżej.spec/08-protocols.md: deklaracyjny podział zadania i protokołu; opis kierunku nie jest dowodem wszystkich wymienionych tam adapterów.compiler/JunexisLang.Compiler.Topology/DistributedTopologyLowerer.cs: wzorce, normalizacja nazw, sprawdzanie zadań i mapa źródeł parametrów.compiler/JunexisLang.Compiler.CodeGen/GoCodeGenerator.cs:EmitRestProtocolBindings,EmitRestParameterBinding,GenerateOpenApi.compiler/JunexisLang.Compiler.Tests/Topology/DistributedTopologyLowererTests.csicompiler/JunexisLang.Compiler.CodeGen.Tests/GoProtocolCodeGenTests.cs: sprawdzenie planu i tekstu wygenerowanych artefaktów.