Połączenia zadań: pipeline
pipeline opisuje nazwany, skierowany graf logicznych połączeń zadań.
Poniższe reguły rozdzielają składnię, analizę grafu i niepełny kontrakt wiązania danych.
Przejście check nie jest dowodem uruchomienia ani poprawnego przesyłania wartości.
Składnia
pipelineDeclaration
: PIPELINE IDENTIFIER LBRACE pipelineConnection* RBRACE
;
pipelineConnection
: USE pipelineSource (ARROW pipelineTarget)? SEMICOLON?
| PARALLEL LBRACE pipelineConnection+ RBRACE
;
pipelineSource
: IDENTIFIER (DOT IDENTIFIER)?
;
pipelineTarget
: IDENTIFIER (DOT IDENTIFIER)?
;
Reguły zapisu i budowania grafu
P01 — deklaracja i połączenie
Deklaracja wymaga nazwy i nawiasów klamrowych. Jej ciało może być puste.
use A -> B zapisuje krawędź skierowaną od zadania A do zadania B.
Średnik po use jest opcjonalny. Jedna instrukcja dopuszcza najwyżej jedną
strzałkę: łańcuch A → B → C wymaga dwóch instrukcji use.
W tej składni nie ma argumentów wywołania, aliasów instancji ani operatora |>.
P02 — nazwy zadań
Analizator grafu rozwiązuje nazwy źródła i celu względem zadań w dokumencie IR,
z rozróżnianiem wielkości liter. Nieznane zadanie powoduje błąd T0002;
nie staje się nowym węzłem. Dotyczy to również źródła w use A bez celu.
Węzłami analizowanego grafu są wszystkie zadeklarowane zadania, także niepołączone.
Graf jest zbierany ze wszystkich deklaracji pipeline; nazwa deklaracji pozostaje
zapisana przy każdej krawędzi. Analiza cykli nie jest ograniczona do jednej deklaracji.
Nie wynika z tego tworzenie osobnej instancji zadania dla każdego wystąpienia use.
P03 — postać z portem
Każda strona dopuszcza dokładnie jeden opcjonalny człon: Task.port.
Zapis oznacza wskazanie nazwanego końca połączenia; parser i IR zachowują obie nazwy.
Zachowanie nazwy portu nie jest jej rozwiązaniem. PipelineGraph.Build sprawdza
istnienie zadań, ale nie istnienie portów ani zgodność ich typów.
Pełna reguła deklarowania wyjść nazwanych, wyboru wejścia przy pominięciu portu
oraz dopasowania wielu parametrów nie jest tu ustalona. Nie należy traktować
dowolnego identyfikatora po kropce jako poprawnego portu tylko dlatego, że check
nie zgłasza błędu.
P04 — źródło bez celu
use A jest dopuszczone przez gramatykę. Analizator sprawdza istnienie A,
ale nie dodaje krawędzi. Ten fakt nie stanowi kontraktu uruchamiania zadania:
nie ustala liczby instancji, argumentów ani momentu startu.
P05 — grupowanie parallel
parallel { ... } wymaga co najmniej jednego połączenia i może zawierać kolejne
bloki parallel. IR zachowuje grupowanie, a analizator grafu zbiera z niego
te same krawędzie co z zapisów bez grupowania.
Sama analiza grafu nie ustanawia bariery synchronizacji, kolejności startu,
liczby wątków, procesów ani fizycznej równoczesności wykonania.
P06 — rozgałęzienia, powtórzenia i cykle
Kilka instrukcji może mieć wspólne źródło lub wspólny cel. Jest to opis topologii,
nie pełna reguła kopiowania wiadomości, rozdzielania pracy czy scalania danych.
Rozgałęzienie nie zmienia samo kontraktu chan, queue lub stream.
Budowanie grafu zachowuje powtórzone krawędzie. Lista następców używana przy analizie cykli usuwa powtórzenia nazw zadań. Nie oznacza to ani podwójnej dostawy, ani gwarantowanej deduplikacji wiadomości podczas wykonania.
Cykl, w tym połączenie zadania z nim samym, wywołuje ostrzeżenie T0001, a nie
błąd. Nie jest to sprawdzenie zakleszczeń, inicjalizacji cyklu ani jego żywotności.
Sprawdzone przykłady
Pary poniżej są sprawdzane przez rzeczywiste CLI w osobnych projektach. Potwierdzają przyjęcie lub odrzucenie źródła; nie są próbami wykonania systemu.
Przykład poprawny
task Emit() @server -> chan<Int> {
send 1
}
task Read(values: chan<Int>) @server {}
pipeline Flow {
use Emit -> Read
}
Kontrola: przykład przyjęty przez check. Nie jest to test wykonania.
Przykład błędny
pipeline Flow { use -> Read }
Powód: Po use wymagana jest nazwa źródłowego zadania.
Kontrola: przykład odrzucony przez check; kody: P0001.
Pełny zakres weryfikacji przykładów.
Przykład: Istniejące i nieznane zadanie
Poprawny zapis
task Emit() @server -> chan<Int> { send 1 }
task Read(values: chan<Int>) @server {}
pipeline P { use Emit -> Read }
Kontrola: przykład przyjęty przez check. Nie jest to test wykonania.
Błędny zapis
task Emit() @server -> chan<Int> { send 1 }
task Read(values: chan<Int>) @server {}
pipeline P { use Emit -> Missing }
Powód: Cel Missing nie jest zadeklarowanym zadaniem; analiza topologii zgłasza T0002.
Kontrola: przykład odrzucony przez check; kody: T0002.
Pełny zakres weryfikacji przykładów.
Przykład: Grupowanie połączeń
Poprawny zapis
task Emit() @server -> chan<Int> { send 1 }
task Read(values: chan<Int>) @server {}
pipeline P { parallel { use Emit -> Read; } }
Kontrola: przykład przyjęty przez check. Nie jest to test wykonania.
Błędny zapis
task Emit() @server -> chan<Int> { send 1 }
task Read(values: chan<Int>) @server {}
pipeline P { parallel {} }
Powód: Ciało parallel musi zawierać co najmniej jedno połączenie; ciało pipeline może być puste.
Kontrola: przykład odrzucony przez check; kody: P0001.
Pełny zakres weryfikacji przykładów.
Przykład: Łańcuch zapisany jako osobne połączenia
Poprawny zapis
task A() @server {}
task B() @server {}
task C() @server {}
pipeline P { use A -> B use B -> C }
Kontrola: przykład przyjęty przez check. Nie jest to test wykonania.
Błędny zapis
task A() @server {}
task B() @server {}
task C() @server {}
pipeline P { use A -> B -> C }
Powód: Jedna instrukcja use dopuszcza najwyżej jedną strzałkę. Ten przykład sprawdza strukturę grafu, nie przesyłanie danych.
Kontrola: przykład odrzucony przez check; kody: P0001.
Pełny zakres weryfikacji przykładów.
Granice obecnego sprawdzania
Dodatkowe próby check dały następujące wyniki:
| Źródło / sytuacja | Wynik | Co z tego wynika |
|---|---|---|
Puste pipeline P {} |
przyjęte | Brak wymogu co najmniej jednej krawędzi. |
use Emit przy istniejącym zadaniu |
przyjęte | Dopuszczony zapis bez celu; graf nie dostaje krawędzi. |
use Emit.unknown -> Read.missing |
przyjęte bez diagnostyk | Brak potwierdzenia istnienia wskazanych portów. |
Wyjście chan<Int> połączone z wejściem queue<Text> |
przyjęte bez diagnostyk | Brak diagnozy zarówno niezgodności rodzaju komunikacji, jak i typu elementu w tej próbie. |
Dwukrotnie use Emit -> Read |
przyjęte bez diagnostyk | Brak zakazu powtórzeń na tym etapie. |
use Emit -> Read i use Read -> Emit |
przyjęte z ostrzeżeniem T0001 |
Wykrycie cyklu nie blokuje check. |
Cel Missing bez deklaracji zadania |
odrzucone, T0002 |
Sprawdzanie nazw zadań jest zaimplementowane. |
Próby z nieznanymi portami i niezgodnymi typami pokazują lukę walidacji,
a nie dozwoloną konwersję. Nie dodajemy na tej podstawie reguły niejawnej
zamiany chan na queue ani Int na Text.
Graf logiczny a generowanie systemu
Analizator liczy rozgałęzienia według unikalnych par (zadanie docelowe, port docelowy),
a zbieganie połączeń według par (zadanie źródłowe, port źródłowy).
Od czterech takich końców zgłasza informacje T0003 lub T0004.
Nie są to limity dopuszczalnej liczby połączeń ani pomiary obciążenia.
DistributedTopologyLowerer tworzy osobny plan rozmieszczenia i transportu.
Obecnie odczytuje rodzaj komunikacji najpierw z typu wyjścia zadania źródłowego;
gdy go brak, sięga do nazwanego parametru celu albo pierwszego parametru przy
pominiętym porcie. Jest to zachowanie tej implementacji, nie kompletny kontrakt
rozwiązywania portów ani dowód zgodności obu końców.
Transformacja do IR ma osobne deskryptory mostów chan, queue i stream
między @server a @report. Ich tworzenie sprawdza rodzaj wyjścia i obecność
parametru komunikacyjnego danego rodzaju; nie zastępuje pełnej walidacji portów
i typów elementów. Sam deskryptor nie dowodzi uruchomienia transportu.
Jawne luki kontraktu
- Reguła deklarowania i rozwiązywania nazwanych wyjść oraz pełne wiązanie wejść, szczególnie przy wielu parametrach i pominiętym porcie.
- Walidacja zgodności rodzaju komunikacji oraz typu elementu na obu końcach; diagnostyki nieistniejących i niejednoznacznych portów.
- Znaczenie powtórzonej krawędzi i współdzielenia zadania między pipeline'ami dla instancji i dostarczania danych.
- Reguły rozsyłania i scalania dla rozgałęzień, niezależne od samego liczenia krawędzi.
- Pełny kontrakt wykonania
paralleliuse Abez celu. - Warunki startu, inicjalizacji i postępu cykli; wykrycie cyklu ich nie rozstrzyga.
Podstawa w repozytorium i zakres dowodów
grammar/JunexisLang.g4: cztery reguły składni zamieszczone powyżej.spec/04-declarations.md, sekcja „CORE: pipeline”: kierunek przepływu i zapis portów.compiler/JunexisLang.Compiler.IR/AstToIrTransformer.cs: zachowanie połączeń, grupowania i budowanie deskryptorów mostów.compiler/JunexisLang.Compiler.Topology/PipelineGraph.cs: nazwy, porty i krawędzie.compiler/JunexisLang.Compiler.Topology/TopologyAnalyzer.cs: cykle i metryki.compiler/JunexisLang.Compiler.Topology/DistributedTopologyLowerer.cs: odrębny plan rozproszony.compiler/JunexisLang.Compiler.Tests/IR/IrTransformerPipelineTests.csoraz testy wcompiler/JunexisLang.Compiler.Tests/Topology/: transformacja i analiza.
Testy struktury i wyniki check nie potwierdzają wykonania wszystkich tych
połączeń w C++, Go i TypeScript ani działania między maszynami.