Importy i zakres nazw
Import wskazuje moduł źródłowy. Nie jest tekstowym wklejeniem pliku. Poniższe reguły odnoszą się do pracy na projekcie z katalogiem modułów; samo parsowanie pojedynczego pliku nie wykonuje całego rozwiązywania importów.
Składnia
importDeclaration
: IMPORT modulePath (AS IDENTIFIER)? SEMICOLON? // import m::p as alias
| IMPORT modulePath LBRACE importSymbolList RBRACE SEMICOLON? // import m::p { X, Y }
;
modulePath
: IDENTIFIER (DOUBLE_COLON IDENTIFIER)*
;
importSymbolList
: importSymbol (COMMA importSymbol)* COMMA?
;
importSymbol
: anyIdentifier (AS anyIdentifier)? // X as alias
;
M01 — położenie i postacie importu
Importy muszą poprzedzać wszystkie deklaracje najwyższego poziomu w pliku.
Średnik jest opcjonalny. Ścieżka to co najmniej jeden identyfikator, z separatorem
::; import a.b nie jest inną pisownią import a::b.
Lista wyboru symboli musi być niepusta i może mieć końcowy przecinek.
Gramatyka nie łączy aliasu całego modułu z listą wyboru w jednej instrukcji.
| Postać | Działanie bindera nazw |
|---|---|
import a::b |
Udostępnia eksportowane deklaracje pod ich nazwami. |
import a::b as x |
Tworzy nazwy widoczne x::Nazwa; zakres ich użycia ogranicza dalsza składnia i implementacja. |
import a::b { X, Y } |
Udostępnia tylko wskazane deklaracje. |
import a::b { X as Z } |
Udostępnia X pod lokalną nazwą Z. |
Wyszukiwanie modułów
M02 — tożsamość modułu
ProjectModuleCatalog odkrywa pliki .jnx rekurencyjnie pod skonfigurowanymi
katalogami źródeł. Ścieżka względem katalogu źródeł, bez rozszerzenia,
tworzy tożsamość: src/domain/types.jnx przy katalogu src daje domain::types.
Import jest rozwiązywany względem tego katalogu tożsamości, nie względem katalogu
pliku importującego. Nie ma tu składni względnego ../ ani nazwy pliku w cudzysłowie.
Segmenty ścieżki pliku muszą spełniać kontrolę identyfikatora w ModuleIdentity:
litera ASCII lub podkreślenie na początku, dalej również cyfry ASCII.
Dodatkowy prefiks przestrzeni nazw katalogu biblioteki jest dołączany do tożsamości.
Rozwiązywanie wersji pakietów i pobieranie źródeł są odrębnymi mechanizmami;
sam import .jnx nie deklaruje wersji ani żądania pobrania z sieci.
Katalog odrzuca tożsamości kolidujące również bez rozróżniania wielkości liter, a także nakładające się własności tego samego pliku przez katalogi źródeł. Te błędy konfiguracji nie są tym samym co błąd symbolu w instrukcji importu.
M03 — brak modułu i wielkość liter
Resolver wymaga dokładnej tożsamości z rozróżnianiem wielkości liter.
Brak modułu daje E0004; istnienie wyłącznie nazwy różniącej się wielkością liter
— E0006. Dotyczy to także Windows; porównanie tożsamości nie jest pozostawione
regułom systemu plików.
Resolver parsuje wszystkie moduły katalogu, również nieosiągalne z pliku wejściowego. Błąd składni istniejącego modułu pozostaje błędem parsera; nie powinien być zastępowany pozornym brakiem importu.
M04 — zależności i cykle
Import tworzy skierowaną zależność modułu importującego od importowanego.
Resolver buduje deterministyczną kolejność z zależnościami przed odbiorcami.
Cykl, także import modułu przez niego samego, jest błędem E0005, z opisem ścieżki.
To kolejność przetwarzania modułów, nie harmonogram uruchomienia ich zadań.
Wiązanie nazw
M05 — eksportowane deklaracje
ProjectSymbolBinder eksportuje lokalne deklaracje struct, enum, fn, task,
extern task, extern type, kontraktów extern oraz storage.
Nie eksportuje przez tę listę pipeline, deployment, device ani protocol.
Nie ma w tej ścieżce modyfikatorów public i private.
Eksporty są zbierane z własnych deklaracji modułu, a nie z jego zakresu po importach. Importowane symbole nie są automatycznie reeksportowane. Moduł korzystający z biblioteki nie udostępnia przez to wszystkich jej nazw swoim odbiorcom.
M06 — wybór, aliasy i kolizje
Wybrana nazwa musi należeć do eksportów wskazanego modułu; brak daje E0007.
Binder porównuje nazwy z rozróżnianiem wielkości liter.
Alias zmienia nazwę widoczną w module odbiorcy, nie nazwę deklaracji źródłowej.
Zakres jest najpierw wypełniany deklaracjami lokalnymi. Import dodaje nazwy;
jeżeli nazwa jest już zajęta, pojawia się E0002. Nie ma tu cichego przesłaniania
przez import. Powtórzenie identycznego importu również może dać kolizję,
zamiast zostać zignorowane.
M07 — granica nazw kwalifikowanych
Binder tworzy nazwy alias::Symbol, ale nie każda pozycja składni je dopuszcza.
W wykonanych próbach wywołanie types::Twice(2) przechodzi check, natomiast
parametr typu types::Reading zostaje odrzucony przez parser.
Nie należy obiecywać pełnego dostępu przez alias modułu we wszystkich konstrukcjach.
Nazwy końców pipeline mają własną składnię i nie są automatycznie rozszerzane
przez fakt utworzenia nazwy w binderze.
Dla aliasu modułu zawierającego storage z nazwanymi zapytaniami binder emituje
ostrzeżenie SQC006: kwalifikowane wywołania tych zapytań nie są udostępniane;
zalecany jest import wybranego modelu storage. Osobny StorageImportAliasRewriter
obsługuje określone aliasy API storage; nie jest ogólnym mechanizmem przemianowania
wszystkich odwołań w programie.
Sprawdzone przykłady
Pary są sprawdzane przez CLI jako osobne projekty z pokazanymi plikami pomocniczymi.
Przejście check nie dowodzi wykonania kodu wygenerowanego dla importowanych deklaracji.
Przykład poprawny
import data::domain { Reading }
task Read(value: Reading) @server {}
Wymagana jednostka modułu data/domain.jnx:
struct Reading { value: Float }
Kontrola: przykład przyjęty przez check. Nie jest to test wykonania.
Przykład błędny
import data.domain { Reading }
Powód: W ścieżce modułu separatorem jest ::, nie kropka.
Kontrola: przykład odrzucony przez check; kody: P0001.
Pełny zakres weryfikacji przykładów.
Przykład: Wybór symbolu i alias lokalny
Poprawny zapis
import domain::types { Reading as Sample }
task Main(value: Sample) @server {}
Wymagana jednostka modułu domain/types.jnx:
struct Reading { value: Int }
fn Twice(value: Int) -> Int { return value * 2 }
Kontrola: przykład przyjęty przez check. Nie jest to test wykonania.
Błędny zapis
import domain::types { Missing }
task Main() @server {}
Powód: Wybrany symbol Missing nie jest eksportowany przez moduł.
Kontrola: przykład odrzucony przez check; kody: E0007.
Pełny zakres weryfikacji przykładów.
Przykład: Tożsamość modułu i wielkość liter
Poprawny zapis
import domain::types
task Main(value: Reading) @server {}
Wymagana jednostka modułu domain/types.jnx:
struct Reading { value: Int }
fn Twice(value: Int) -> Int { return value * 2 }
Kontrola: przykład przyjęty przez check. Nie jest to test wykonania.
Błędny zapis
import Domain::types
task Main() @server {}
Powód: Ścieżka Domain::types różni się wielkością liter od domain::types.
Kontrola: przykład odrzucony przez check; kody: E0006.
Pełny zakres weryfikacji przykładów.
Przykład: Kolizja z deklaracją lokalną
Poprawny zapis
import domain::types
task Main(value: Reading) @server {}
Wymagana jednostka modułu domain/types.jnx:
struct Reading { value: Int }
fn Twice(value: Int) -> Int { return value * 2 }
Kontrola: przykład przyjęty przez check. Nie jest to test wykonania.
Błędny zapis
import domain::types
struct Reading { other: Bool }
Powód: Importowana nazwa Reading koliduje z lokalną deklaracją.
Kontrola: przykład odrzucony przez check; kody: E0002.
Pełny zakres weryfikacji przykładów.
Przykład: Niedostępny moduł
Poprawny zapis
import domain::types
task Main(value: Reading) @server {}
Wymagana jednostka modułu domain/types.jnx:
struct Reading { value: Int }
fn Twice(value: Int) -> Int { return value * 2 }
Kontrola: przykład przyjęty przez check. Nie jest to test wykonania.
Błędny zapis
import absent
task Main() @server {}
Powód: Modułu absent nie ma w katalogu projektu.
Kontrola: przykład odrzucony przez check; kody: E0004.
Pełny zakres weryfikacji przykładów.
Przykład: Cykl importów
Poprawny zapis
task Main() @server {}
Wymagana jednostka modułu helper.jnx:
import main
struct Helper { value: Int }
Kontrola: przykład przyjęty przez check. Nie jest to test wykonania.
Błędny zapis
import helper
task Main() @server {}
Powód: main importuje helper, a helper importuje main; resolver odrzuca cykl E0005.
Kontrola: przykład odrzucony przez check; kody: E0005.
Pełny zakres weryfikacji przykładów.
Składanie programu a widoczność
CLI rozwiązuje katalog modułów, wiąże symbole i sprawdza każdy moduł w jego zakresie.
Dopiero potem ProjectProgramComposer składa osiągalne moduły do wspólnego AST,
zależności przed odbiorcami. Z modułów bibliotecznych wybiera wymienione wyżej
rodzaje deklaracji; deklaracje organizujące system zachowuje z modułu wejściowego.
Osiągalność jest liczona po importach modułów, nie po faktycznie użytych symbolach. Import wybranej deklaracji nie dowodzi więc usunięcia wszystkich pozostałych deklaracji biblioteki z programu wynikowego.
Composer dodaje wybrane aliasy deklaracji widoczne w module wejściowym, zmieniając nazwę deklaracji. Nie przeprowadza ogólnego przemianowania wszystkich jej wewnętrznych odwołań. Zachowanie aliasów rekurencyjnych funkcji, wzajemnych referencji i bibliotek z kolidującymi nazwami wymaga oddzielnych dowodów; samo poprawne wiązanie zakresu nie wystarcza.
Wyniki dodatkowych prób i jawne luki
| Próba | Wynik check | Wniosek |
|---|---|---|
| Zwykły import i użycie typu Reading | przyjęte | Typ jest widoczny w module odbiorcy. |
| Wybór Reading jako Sample i użycie Sample | przyjęte | Ta ścieżka aliasu przechodzi sprawdzanie. |
Typ types::Reading po aliasie modułu |
odrzucone, P0001 |
Składnia typu nie zapewnia tej postaci kwalifikacji. |
Wywołanie types::Twice(2) |
przyjęte | Nie stanowi dowodu poprawnego kodu wynikowego. |
| Dwukrotny import tych samych eksportów | odrzucone, E0002 |
Brak automatycznego ignorowania powtórzenia. |
| Typ Reading bez importu istniejącego modułu | przyjęte z diagnostyką E0021 poziomu warning |
Nierozwiązany typ nie blokuje tej próby check. |
| Import po deklaracji lub pusta lista wyboru | odrzucone, P0001 |
Ograniczenia gramatyki. |
Pozostają jawne luki: pełny kontrakt użycia nazw kwalifikowanych we wszystkich pozycjach, zgodność aliasów z generowaniem i wykonaniem, izolacja nazw po złożeniu modułów oraz kompletność diagnozowania nierozwiązanych nazw. Nie ustanawiamy nowych reguł na podstawie samego braku błędu.
Podstawa w repozytorium
grammar/JunexisLang.g4: program, importy i ścieżki modułów.compiler/JunexisLang.Compiler.Core/Projects/ModuleIdentity.csorazProjectModuleCatalog.cs: tożsamości i katalog źródeł.compiler/JunexisLang.Compiler.Core/Projects/ProjectImportResolver.cs: rozwiązywanie modułów, wielkość liter i cykle.compiler/JunexisLang.Compiler.Core/Projects/ProjectSymbolBinder.cs: eksporty, zakresy, aliasy i kolizje.compiler/JunexisLang.Compiler.Core/Projects/ProjectProgramComposer.csorazStorageImportAliasRewriter.cs: składanie programu i ograniczony zapis aliasów.compiler/JunexisLang.Compiler.CLI/Project/CliProjectChecker.cs: projektowa ścieżka check.- Testy
ProjectModuleCatalogTests,ProjectImportResolverTestsiProjectSymbolBinderTestswcompiler/JunexisLang.Compiler.Tests/Projects/.