Przejdź do treści

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.cs oraz ProjectModuleCatalog.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.cs oraz StorageImportAliasRewriter.cs: składanie programu i ograniczony zapis aliasów.
  • compiler/JunexisLang.Compiler.CLI/Project/CliProjectChecker.cs: projektowa ścieżka check.
  • Testy ProjectModuleCatalogTests, ProjectImportResolverTests i ProjectSymbolBinderTests w compiler/JunexisLang.Compiler.Tests/Projects/.