Stan, konfiguracja i czas
state deklaruje nazwany stan z jawnym typem. Rozdział rozdziela intencję stanu
zadania, rzeczywiste mechanizmy generatorów i zakres dowodów. Zachowanie wartości
w kolejnych wywołaniach nie jest automatycznie dowodem izolacji instancji,
trwałości po awarii ani bezpieczeństwa współbieżnego dostępu.
Składnia
stateDeclaration
: STATE IDENTIFIER COLON typeReference (ASSIGN expression)? SEMICOLON?
;
configBlock
: CONFIG LBRACE configField* RBRACE
;
configField
: IDENTIFIER COLON typeReference (COLON literal)? (SEMICOLON | COMMA)?
;
everyStatement
: EVERY expression LBRACE statement* RBRACE
;
delayStatement
: DELAY expression SEMICOLON?
;
S01 — deklaracja i aktualizacja
state nazwa: Typ wymaga nazwy i typu. Inicjalizator = wyrażenie oraz średnik
są opcjonalne. Zwykłe count = count + 1 jest aktualizacją, a nie ponowną
deklaracją stanu. Ponowne state count: ... nie ma ustalonej semantyki resetu.
Gramatyka dopuszcza state jako członek zadania i jako instrukcję w blokach.
Przyjęcie zagnieżdżonego state w if nie dowodzi, że wszystkie etapy zbierają
je i przechowują tak samo jak stan bezpośrednio w ciele zadania.
S02 — inicjalizacja i walidacja
Intencją inicjalizatora jest określenie wartości początkowej stanu. Generatory mają mechanizmy tworzenia stanu domyślnego i odczytu wcześniej zapamiętanego stanu. Nie wolno z tego wyprowadzać jednej, kompletnej reguły „dokładnie raz na instancję” bez określenia, czym jest instancja na danej ścieżce wykonania.
W analizatorze bezpośrednia deklaracja state najpierw wprowadza nazwę do zakresu,
a potem waliduje wyrażenie inicjalizatora. Ta gałąź nie porównuje typu inicjalizatora
z zadeklarowanym typem tak jak kontrola jawnego let. Wykonane próby przyjęły
bez diagnostyk state count: Int = "text", samoodwołanie state count: Int = count
oraz dwie deklaracje count. Są to luki kontroli, nie dopuszczone reguły konwersji,
rekurencyjnej inicjalizacji lub przesłaniania stanu.
Przy braku inicjalizatora generatory sięgają po własne procedury wartości zerowej. Nie ustanawiamy na tej podstawie wspólnego kontraktu wartości domyślnych wszystkich typów, w szczególności uchwytów extern i typów złożonych.
Czas życia i przechowywanie
S03 — Go / server
GoCodeGenerator emituje typ stanu zadania, wskaźnik pamięci podręcznej nazwany
według zadania oraz funkcje tworzenia, ładowania i zapisywania stanu.
Odczyt najpierw korzysta z pamięci podręcznej. Jeśli profil pozwala na plikowe I/O,
próbuje wczytać <NazwaZadania>.state.json; przy niepowodzeniu odczytu lub
niepoprawnym JSON przechodzi do wartości domyślnej.
Zapis aktualizuje pamięć podręczną i — przy obsłudze plików — serializuje JSON.
W tej ścieżce wynik os.WriteFile jest ignorowany. Nie ma tu dowodu atomowego
zapisu, trwałości na dysku, migracji schematu, poprawnego odtworzenia po awarii
ani jawnego przekazania błędu do zadania.
Mechanizm jest nazwany według zadania, nie identyfikatora instancji lub użytkownika. Nie jest sam w sobie dowodem osobnego stanu replik ani synchronizacji równoległych wywołań. Odczyt i zapis struktury nie ustanawiają transakcji całego przejścia stanu.
S04 — TypeScript / report
Generator tworzy pamięć podręczną według nazwy zadania i klucz
junexis.state.<NazwaZadania>. Przy dostępności lokalnego przechowywania korzysta
z localStorage; ładuje JSON i zwraca płytką kopię stanu. Niepoprawny JSON jest
pomijany z powrotem do pamięci podręcznej lub wartości domyślnej.
Rzutowanie wyniku JSON na typ TypeScript nie jest walidacją schematu danych w czasie wykonania. Płytka kopia nie dowodzi izolacji zagnieżdżonych obiektów. Sam klucz nie rozróżnia kont, lekcji, replik ani wielu instancji tej samej nazwy. Nie ustanawiamy przez to wspólnego kontraktu synchronizacji kart lub urządzeń.
S05 — granica izolacji i restartu
Testy generatorów oraz scenariusz kolejnych wywołań potwierdzają określone mechanizmy zachowania wartości. Nie potwierdzają uniwersalnej zasady niezależnego stanu każdej instancji zadania. Dotyczy to również deklaracji liczby replik w deployment.
Trzeba odróżniać ponowne wywołanie funkcji, kolejny takt, nowy proces, ponowne załadowanie strony i utworzenie nowej instancji systemu. Test jednego z tych przypadków nie jest dowodem dla pozostałych. Z samego state nie wynika zapis w NVRAM, replikacja ani odzyskanie po utracie zasilania.
Konfiguracja i czas
S06 — config
config jest osobnym blokiem typowanych pól, nie synonimem modyfikowanego stanu.
Wartość domyślna w tej składni występuje po drugim dwukropku i jest literałem,
np. limit: Int : 10. Nie jest to zapis = wyrażenie jak przy state.
Obsługa zmian konfiguracji i ich wpływu na już istniejący stan wymaga osobnego
kontraktu; samo pole config nie oznacza migracji ani resetu stanu.
S07 — every i delay
every okres { ... } wymaga bloku. delay przyjmuje wyrażenie.
W scenariuszu kontraktu junexis.task-lifecycle/0.1 pierwszy takt następuje po
okresie, a anulowanie jest obserwowane między zakończonymi taktami.
To zakres ograniczonego scenariusza, nie dowód pełnego wykonawcy cyklu życia.
Próby every 1 {} i delay 1 przeszły check bez diagnostyk. Parser przyjmuje
wyrażenia, a ta próba nie potwierdza wymagania Duration ani jednostki liczby 1.
Nie ustanawiamy nieudokumentowanej konwersji, polityki dla zera i liczb ujemnych,
nadrabiania taktów ani gwarancji czasu rzeczywistego.
Sprawdzone przykłady
Pary sprawdza rzeczywiste CLI w osobnych projektach, bez uruchamiania zadania.
Przykład poprawny
task Counter() @server {
state count: Int = 0
every 1s {
count = count + 1
}
}
Kontrola: przykład przyjęty przez check. Nie jest to test wykonania.
Przykład błędny
task Counter() @server { state count = 0 }
Powód: Deklaracja state wymaga jawnego typu po dwukropku.
Kontrola: przykład odrzucony przez check; kody: P0001.
Pełny zakres weryfikacji przykładów.
Przykład: Jawny typ stanu
Poprawny zapis
task Main() @server { state count: Int }
Kontrola: przykład przyjęty przez check. Nie jest to test wykonania.
Błędny zapis
task Main() @server { state count = 0 }
Powód: Deklaracja state wymaga dwukropka i typu, także przy obecnym inicjalizatorze.
Kontrola: przykład odrzucony przez check; kody: P0001.
Pełny zakres weryfikacji przykładów.
Przykład: Aktualizacja w bloku okresowym
Poprawny zapis
task Pulse() @server { state count: Int = 0 every 250ms { count = count + 1 } }
Kontrola: przykład przyjęty przez check. Nie jest to test wykonania.
Błędny zapis
task Pulse() @server { every 250ms count = 1 }
Powód: every wymaga ciała w nawiasach klamrowych.
Kontrola: przykład odrzucony przez check; kody: P0001.
Pełny zakres weryfikacji przykładów.
Co rzeczywiście sprawdzają testy wykonania
Repozytorium zawiera scenariusz
GeneratedLifecycleStateAndTime_IsEquivalentAcrossCppGoAndTypeScriptAsync.
Wywołuje wygenerowane zadanie Advance z kolejnymi argumentami 2, 3, 4,
przy stanie początkowym 10, i oczekuje wartości 12, 15, 19; sprawdza również
reprezentację interwału 250 ms.
Lista zdarzeń start/tick/stop w tym teście jest wpisana w kodzie testowym. Nie jest dowodem, że wykonawca sam uruchomił hooki lub wykonał anulowanie. Scenariusz nie sprawdza wielu izolowanych instancji, równoległych wywołań, awarii zapisu ani restartu procesu. W tym przeglądzie nie powtarzamy tej próby natywnej; jej zakres ustalono z kodu testu.
EveryLowering_WaitsBeforeFirstTickOnEveryBackendAsync sprawdza tekst generowania
oczekiwania przed taktem. StatefulEvery_PersistsEachCompletedTickOnGoAndTypeScriptAsync
sprawdza obecność aktualizacji i wywołań zapisu. Nie są pomiarem czasu ani testem
trwałości po odłączeniu zasilania.
Jawne luki
- Typowanie inicjalizatorów, duplikaty, samoodwołania i zagnieżdżone deklaracje state.
- Jednoznaczna tożsamość instancji i izolacja stanu między replikami, kontami i uruchomieniami.
- Atomowość przejść, bezpieczeństwo współbieżnych wywołań i konfliktów zapisu.
- Pełne reguły wartości domyślnych, ponownej inicjalizacji i resetu.
- Walidacja, wersjonowanie i migracja zapisanego schematu oraz raportowanie błędów.
- Trwałość po awarii i przenoszenie stanu między targetami lub hostami.
- Walidacja czasu, przeciążenia harmonogramu, anulowanie i dokładność zegara.
Nie zastępujemy tych reguł zachowaniem pliku JSON, localStorage albo pojedynczego wywołania wygenerowanej funkcji.
Podstawa w repozytorium
grammar/JunexisLang.g4: state, config i instrukcje czasu.compiler/JunexisLang.Compiler.Core/Semantics/SemanticAnalyzer.cs: sprawdzanie ciała zadania i inicjalizatorów state.compiler/JunexisLang.Compiler.IR/TaskLifecycleRuntimeContract.cs: ograniczony kontrakt sekwencji zdarzeń.compiler/JunexisLang.Compiler.CodeGen/GoCodeGenerator.csiTypeScriptCodeGenerator.cs: pamięć podręczna oraz odczyt i zapis stanu.compiler/JunexisLang.Compiler.CodeGen.Tests/GeneratedLifecycleStateTimeConformanceTests.cs,CodeGeneratorGoStateTests.cs,CodeGeneratorTypeScriptStateTests.cs: scenariusz wykonania i testy generowanych mechanizmów.