Zadania
task deklaruje nazwaną jednostkę zachowania: jej wejścia, opcjonalny wynik,
adnotacje platformy i ciało. Ten rozdział oddziela deklarację, wykonanie
oraz instancję ze stanem. Nie traktuje mechanizmu jednego backendu jako reguły języka.
1. Składnia deklaracji
taskDeclaration
: TASK IDENTIFIER
(LPAREN parameterList? RPAREN)?
platformAnnotation*
communicationSignature?
taskBody
;
parameterList
: parameter (COMMA parameter)*
;
parameter
: anyIdentifier COLON typeReference (ASSIGN expression)?
;
communicationSignature
: ARROW typeReference
;
Reguły poprawności składniowej
| Reguła | Wymaganie |
|---|---|
| T01 | Po task musi wystąpić identyfikator nazwy. |
| T02 | Lista parametrów może zostać pominięta albo ujęta w nawiasy. Każdy parametr wymaga nazwy i typu po :. |
| T03 | Wartość domyślna parametru ma postać = wyrażenie. Sama możliwość zapisu nie rozstrzyga wszystkich zasad jej podstawiania. |
| T04 | Adnotacje platformy występują po liście parametrów, przed wynikiem. Gramatyka dopuszcza ich brak lub więcej niż jedną. |
| T05 | Typ wyniku jest opcjonalny. Jeżeli występuje strzałka ->, musi po niej wystąpić typ. |
| T06 | Zwykły task zawsze ma ciało w { ... }; ciało może być puste. |
task Main @server {} oraz task Main() @server {} są przyjmowane przez obecną
gramatykę i check. Nie ustanawiamy tu wymagania pustych nawiasów, którego nie ma w źródłach.
Deklarację bez ciała zapewnia odrębna konstrukcja extern task. Nie jest to skrócona postać zwykłego zadania.
2. Deklaracja, instancja i uruchomienie
| Pojęcie | Ustalone znaczenie | Granica definicji |
|---|---|---|
| Deklaracja | Nazwany opis wejść, wyniku i zachowania. | Samo wystąpienie deklaracji nie dowodzi uruchomienia na konkretnej maszynie. |
| Instancja | Kontekst wykonania i stanu, do którego odwołuje się kontrakt cyklu życia. | Brak kompletnej reguły tworzenia wielu instancji, ich tożsamości i izolacji. |
| Wywołanie | Wykonanie zadania z argumentami; repo zawiera wykonywalny scenariusz powtarzanych wywołań zadania wartościowego. | Nie jest ogólną obietnicą nowej instancji przy każdym wywołaniu. |
| Połączenie w pipeline | Ustanowienie przepływu między zadaniami. | Nie określa samo liczby procesów, schedulera ani trwałości transportu. |
Co uruchamia zadanie?
W repozytorium występują uruchomienie przez wygenerowany punkt wejścia, wywołanie zadania z argumentami i podłączenie go do pipeline. Nie ma jednak jednego domkniętego kontraktu uruchamiania wszystkich tych postaci na wszystkich targetach. Ta luka pozostaje jawna. Nie zastępujemy jej regułą „każda deklaracja uruchamia się automatycznie”.
Odebranie danych jest kolejnym zagadnieniem: recv lub iteracja komunikacyjna
mogą oczekiwać na wejście już uruchomionego zadania. Dostarczenie wiadomości
nie jest tym samym co utworzenie nowej instancji.
3. Wejścia i wynik
Parametr ma postać nazwa: Typ. Typ może być wartościowy, np. Int, albo
komunikacyjny, np. chan<Int>; nie każdy parametr jest kanałem.
Parametr komunikacyjny udostępnia źródło danych zgodnie z kontraktem swojego typu.
| Zapis | Znaczenie |
|---|---|
value: Int |
Wartościowe wejście o typie Int. |
values: chan<Int> |
Wejście komunikacyjne, z którego zadanie może odbierać liczby. |
-> Int |
Zadeklarowany wynik wartościowy. return wyprowadza wynik z wykonania. |
-> chan<Int> |
Zadeklarowane wyjście komunikacyjne. send emituje wiadomość. |
Brak -> Typ |
Brak jawnej deklaracji wyniku; nie dopisujemy niejawnego wyniku na podstawie języka hosta. |
send 1 i return 1 nie są zamienne. Zgodność typów wartości wejścia, wyniku
i wiadomości wymaga kontroli znaczenia. Poprawny zapis składniowy nie dowodzi,
że obecny check diagnozuje każdą niezgodność.
Reguły połączenia źródła i odbiorcy określa pipeline, a własności dostarczania — komunikacja.
4. Ciało zadania
taskBody
: LBRACE taskMember* RBRACE
;
taskMember
: stateDeclaration
| configBlock
| nativeSection // embedded library deps — spójne z device.native (D13)
| lifecycleHook
| statement
;
| Element | Rola |
|---|---|
state nazwa: Typ = wyrażenie |
Stan zadania. |
config { ... } |
Typowane pola konfiguracji. |
native { ... } |
Deklaracje zależności natywnych; nie dowolny blok instrukcji Junexis. |
on nazwa { ... } |
Nazwany hak cyklu życia. |
| Instrukcje | Obliczenia, przepływ sterowania, komunikacja i zakończenie wykonania. |
config i state nie są synonimami. Wartość domyślna pola konfiguracji jest
w gramatyce poprzedzona drugim dwukropkiem, np. period: Duration : 1s.
Inicjalizacja stanu używa =, np. state count: Int = 0.
configBlock
: CONFIG LBRACE configField* RBRACE
;
configField
: IDENTIFIER COLON typeReference (COLON literal)? (SEMICOLON | COMMA)?
;
5. Stan i cykl życia
Kontrakt junexis.task-lifecycle/0.1 opisuje ograniczony scenariusz okresowy:
- Inicjalizacja stanu następuje raz na instancję zadania.
- Wartości stanu są zachowane między kolejnymi wywołaniami tego kontekstu.
- Zdarzenie
startpoprzedza wykonania okresowe;tickoznacza zakończony takt. - Pierwszy takt
everynastępuje po interwale. - Anulowanie jest obserwowane pomiędzy zakończonymi taktami, a
stopwystępuje po jego zaobserwowaniu.
Dla trzech zakończonych taktów kontrakt podaje kolejność
start → tick → tick → tick → stop. Nie jest to uniwersalna definicja
wszystkich zadań, haków i wyjątkowych zakończeń.
Granice gwarancji stanu
- Stan między wywołaniami nie oznacza automatycznie zapisu na dysk, odtworzenia po restarcie ani przetrwania utraty zasilania.
- Kontrakt nie definiuje tutaj izolacji kilku równoległych instancji tej samej deklaracji.
- Nie ma ogólnej gwarancji transakcyjności aktualizacji wobec innych zadań.
- Zachowanie bloku
everynie gwarantuje dokładności zegara ściennego ani twardego czasu rzeczywistego.
Szczegóły zapisu i ograniczeń są w rozdziale o stanie i czasie.
Klasyfikacja cyklu życia a zachowanie
Typed Semantic IR rozróżnia OneShot, Periodic, Looping, HookDriven
i Mixed. Projektor klasyfikuje obecność every, jawnego loop i haków.
To klasyfikacja opisu, nie scheduler. W szczególności iteracja
for value in values po kanale nie staje się jednoelementowym odbiorem tylko
z powodu braku jawnego loop w tej klasyfikacji.
6. Haki cyklu życia
lifecycleHook
: ON IDENTIFIER LBRACE statement* RBRACE
;
Ustalona składnia wymaga on, identyfikatora i bloku. Starszy dokument deklaracji
wymienia start, connect, reconnect i disconnect. Sam parser nie ogranicza
jednak identyfikatora do tego katalogu.
Luka walidacji: kontrolna jednostka z on imaginary_event { ... } została
przyjęta przez bieżący check. Nie potwierdziliśmy starszej obietnicy odrzucenia
nieznanego haka kodem E0013. Przyjęcie tej jednostki nie dowodzi, że hak zostanie wykonany.
Zdarzenie stop w kontrakcie śladu cyklu życia również nie ustanawia samo
obsługi składni on stop. Nie utożsamiamy tych dwóch pojęć.
7. Znaczenie platformy
Adnotacja wskazuje kontekst wykonania: urządzenie, serwer lub raport. Nie przydziela sama identyfikatora hosta, procesu, wątku ani rdzenia. Przeniesienie zadania nie usuwa jego zależności natywnych lub ograniczeń komunikacji.
Próby bez adnotacji i z jednoczesnymi @server @embedded są przyjmowane przez
check. Pierwsza jest dopuszczona przez platformAnnotation*; druga nie dowodzi,
że istnieje kompletna semantyka jednoczesnego wykonania takiej deklaracji na dwóch targetach.
8. Przykłady i ich zakres
Poniższe jednostki sprawdzono poleceniem check. Przykłady błędne są odrzucane
z diagnostyką języka. Nie przypisujemy im niezweryfikowanych błędów znaczeniowych.
Przykład poprawny
task Emit() @server -> chan<Int> {
send 1
}
Kontrola: przykład przyjęty przez check. Nie jest to test wykonania.
Przykład błędny
task Emit() @server -> chan<Int>
Powód: Zwykła deklaracja task wymaga ciała. Deklaracja bez ciała wymaga extern task.
Kontrola: przykład odrzucony przez check; kody: P0001.
Pełny zakres weryfikacji przykładów.
Przykład: Parametr i wynik wartościowy
Poprawny zapis
task Twice(value: Int = 2) @server -> Int {
return value * 2
}
Kontrola: przykład przyjęty przez check. Nie jest to test wykonania.
Błędny zapis
task Twice(value) @server -> Int { return value * 2 }
Powód: Parametr zadania wymaga jawnego typu po dwukropku.
Kontrola: przykład odrzucony przez check; kody: P0001.
Pełny zakres weryfikacji przykładów.
Przykład: Wejście komunikacyjne
Poprawny zapis
task Read(values: chan<Int>) @server {
for value in values {
log(value)
}
}
Kontrola: przykład przyjęty przez check. Nie jest to test wykonania.
Błędny zapis
task Read(values: chan<Int>) @server { for value values { log(value) } }
Powód: Iteracja ma postać for nazwa in wyrażenie; pominięto słowo in.
Kontrola: przykład odrzucony przez check; kody: P0001.
Pełny zakres weryfikacji przykładów.
Przykład: Stan i wykonanie okresowe
Poprawny zapis
task Pulse() @server {
state count: Int = 0
every 1s {
count = count + 1
log(count)
}
}
Kontrola: przykład przyjęty przez check. Nie jest to test wykonania.
Błędny zapis
task Pulse() @server { state count = 0 }
Powód: Deklaracja stanu wymaga typu. state count = 0 nie jest poprawną deklaracją state.
Kontrola: przykład odrzucony przez check; kody: P0001.
Pełny zakres weryfikacji przykładów.
Przykład: Zewnętrzna postać haka
Poprawny zapis
task Started() @server {
on start {
log("started")
}
}
Kontrola: przykład przyjęty przez check. Nie jest to test wykonania.
Błędny zapis
task Started() @server { on start log("started") }
Powód: Hak on nazwa wymaga bloku instrukcji w nawiasach klamrowych.
Kontrola: przykład odrzucony przez check; kody: P0001.
Pełny zakres weryfikacji przykładów.
9. Jawne luki specyfikacji
| Pytanie | Czego nie rozstrzygają sprawdzone źródła |
|---|---|
| Ile powstaje instancji? | Pełne reguły instancjonowania przez wywołania, wiele pipeline i różne targety. |
| Jak izolowany jest stan? | Tożsamość instancji, równoległe wywołania i relacja do odtwarzania stanu. |
| Jak zaczyna i kończy się każde zadanie? | Jeden model dla zadań wartościowych, konsumentów, haków i modeli mieszanych. |
| Co oznacza nieznany hak? | Ujednolicona lista, walidacja i warunki wywołania we wszystkich kontekstach. |
| Jak działają parametry domyślne? | Pełne zasady podstawiania w wywołaniach, połączeniach i wiązaniach protokołu. |
| Jak kończy się odbiór? | Ogólne zasady zamknięcia wejścia, błędu, anulowania i kolejności efektów końcowych. |
Granice bieżącej implementacji i dowodów
To obserwacje implementacji, nie dodatkowe reguły języka:
- Generator Go wiąże stan i jego cache z nazwą zadania. Nie jest to dowód izolacji wielu niezależnych instancji.
- Generator embedded pozostawia zadania wymagające parametrów bez gotowego doprowadzenia wejść w punkcie startowym. Nie wolno obiecywać uruchomienia dowolnego konsumenta tylko na podstawie adnotacji.
- W teście natywnym
GeneratedLifecycleStateAndTime_IsEquivalentAcrossCppGoAndTypeScriptAsyncrzeczywiste wywołania generowanych zadań potwierdzają wartości stanu12, 15, 19oraz interwał250 ms. Lista zdarzeń cyklu życia jest wpisana w kod otoczki testu; ten test nie jest samodzielnym pomiarem uruchamiania haków i anulowania. - Osobne testy
EveryLowering_WaitsBeforeFirstTickOnEveryBackendAsynciStatefulEvery_PersistsEachCompletedTickOnGoAndTypeScriptAsyncsprawdzają wygenerowany kod. Ich wyniku nie należy nazywać pełnym testem wykonania.
10. Podstawa reguł i sposób sprawdzenia
| Źródło w repozytorium | Zakres |
|---|---|
grammar/JunexisLang.g4: taskDeclaration, taskMember, parameter, lifecycleHook |
Zewnętrzna składnia i składniki ciała. |
spec/04-declarations.md: „CORE: task” |
Opis znaczenia, konfiguracji i deklarowany katalog haków; z wyżej wskazaną rozbieżnością walidacji. |
spec/22-semantic-conformance.md: „Lifecycle tasku, stan i czas” |
Zakres kontraktu scenariusza okresowego. |
conformance/semantic-scenarios.json: runtime.task-lifecycle-state-time |
Zadeklarowane własności stanu i czasu. |
compiler/JunexisLang.Compiler.IR/TaskLifecycleRuntimeContract.cs |
Wersja kontraktu i kanoniczna kolejność zdarzeń ograniczonego scenariusza. |
compiler/JunexisLang.Compiler.IR/TypedSemanticIR.cs: ClassifyLifecycle |
Sposób klasyfikacji zadania, nie uruchamianie. |
Testy TaskLifecycleRuntimeContractTests, TypedSemanticProjectorTests, GeneratedLifecycleStateTimeConformanceTests |
Różne poziomy kontroli kontraktu, projekcji i generowania; ich zakres opisano powyżej. |
Rozdział nie zmienia gramatyki ani zachowania wykonawcy. Luki nie są rozstrzygane przez dopisanie zachowania, którego źródła nie ustalają.