Iter 5a: Workspace-Loader mit Imports

ailang_core::Workspace + load_workspace folgt imports-Feld rekursiv
vom Eintrittsmodul. DFS mit Zyklus-Erkennung; Konvention: Modulname
== Dateiname (.ail.json). WorkspaceLoadError sammelt strukturiert:
Cycle, ModuleNotFound, ModuleNameMismatch, Schema, Io. Neues
ail workspace <entry> [--json] listet erreichbare Module mit
module_hash und Def-Count. Bestehende Subkommandos arbeiten weiter
pro Einzelmodul (Iter 5d).
This commit is contained in:
2026-05-07 11:23:21 +02:00
parent 7619f20cd6
commit 3451b5bd15
6 changed files with 475 additions and 0 deletions
+65
View File
@@ -83,6 +83,16 @@ enum Cmd {
#[arg(long)]
json: bool,
},
/// Lädt einen Workspace (Eintrittsmodul + transitive Imports) und
/// listet alle erreichbaren Module mit Hash und Def-Anzahl.
///
/// Iter 5a: nur das Listing. Cross-Module-Typcheck/Codegen folgt in
/// 5b/5c; bestehende Subkommandos arbeiten weiter pro Einzelmodul.
Workspace {
entry: PathBuf,
#[arg(long)]
json: bool,
},
}
fn main() -> Result<()> {
@@ -286,6 +296,61 @@ fn main() -> Result<()> {
std::process::exit(1);
}
}
Cmd::Workspace { entry, json } => {
let ws = ailang_core::load_workspace(&entry)?;
// Alphabetisch über Modul-Namen iterieren (BTreeMap-Order ist
// bereits sortiert; explizit absichern).
let mut entries: Vec<(String, String, usize)> = ws
.modules
.iter()
.map(|(name, m)| {
(
name.clone(),
ailang_core::module_hash(m),
m.defs.len(),
)
})
.collect();
entries.sort_by(|a, b| a.0.cmp(&b.0));
if json {
let arr: Vec<_> = entries
.iter()
.map(|(name, hash, defs)| {
serde_json::json!({
"name": name,
"hash": hash,
"defs": defs,
})
})
.collect();
let out = serde_json::json!({
"entry": ws.entry,
"modules": arr,
});
println!("{}", serde_json::to_string_pretty(&out)?);
} else {
// Spaltenbreite an längstem Modulnamen ausrichten. Erste Zeile
// markiert das Eintrittsmodul mit `*`.
let name_width = entries
.iter()
.map(|(n, _, _)| n.len())
.max()
.unwrap_or(0)
.max(6);
println!("entry: {}", ws.entry);
for (name, hash, defs) in &entries {
let marker = if *name == ws.entry { "*" } else { " " };
println!(
"{marker} {:<width$} {} {:>3} defs",
name,
hash,
defs,
width = name_width,
);
}
}
}
Cmd::Deps { path, of, json } => {
let m = ailang_core::load_module(&path)?;
let mut entries = Vec::new();