Translate project content to English
Make English the project-wide language: all comments, string literals, CLI help text, design docs, journal, agent prompts, and README. Only CLAUDE.md (the user's own instruction file) stays German, and the live conversation between user and Claude continues in German. Adds a new "Project language: English" section to docs/DESIGN.md as the durable convention. No logic changes — translation only. Four internal error strings in the typechecker were retranslated; no test asserts on their wording. Verified: cargo test --workspace passes (44/44). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
//! Workspace-Loader: lädt ein Eintrittsmodul und folgt rekursiv dessen
|
||||
//! `imports`. Konvention: ein `import { module: "foo" }` wird relativ zum
|
||||
//! Verzeichnis des Eintrittsfiles als `<root_dir>/foo.ail.json` aufgelöst.
|
||||
//! Workspace loader: loads an entry module and recursively follows its
|
||||
//! `imports`. Convention: an `import { module: "foo" }` is resolved
|
||||
//! relative to the entry file's directory as `<root_dir>/foo.ail.json`.
|
||||
//!
|
||||
//! Iter 5a hat die Verantwortung, alle erreichbaren Module zu **finden** und
|
||||
//! konsistent zu **laden**. Cross-Modul-Typcheck (5b) und -Codegen (5c) bauen
|
||||
//! darauf auf, sind aber nicht Teil dieses Schritts.
|
||||
//! Iter 5a is responsible for **finding** and consistently **loading**
|
||||
//! all reachable modules. Cross-module typecheck (5b) and codegen (5c)
|
||||
//! build on top, but are not part of this step.
|
||||
|
||||
use crate::ast::Module;
|
||||
use crate::canonical;
|
||||
@@ -12,12 +12,12 @@ use crate::{load_module, Error as CoreError};
|
||||
use std::collections::{BTreeMap, HashSet};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
/// Vollständig geladener Workspace.
|
||||
/// Fully loaded workspace.
|
||||
///
|
||||
/// `entry` benennt das Eintrittsmodul (Modulname, **nicht** Pfad). Alle
|
||||
/// transitiv erreichbaren Module sind in `modules` enthalten und per
|
||||
/// `Module.name` indiziert. `root_dir` ist das Verzeichnis, in dem das
|
||||
/// Eintrittsfile liegt; alle Imports werden relativ dazu aufgelöst.
|
||||
/// `entry` names the entry module (module name, **not** a path). All
|
||||
/// transitively reachable modules are contained in `modules` and indexed
|
||||
/// by `Module.name`. `root_dir` is the directory the entry file lives
|
||||
/// in; all imports are resolved relative to it.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Workspace {
|
||||
pub entry: String,
|
||||
@@ -25,10 +25,10 @@ pub struct Workspace {
|
||||
pub root_dir: PathBuf,
|
||||
}
|
||||
|
||||
/// Strukturierte Fehler des Workspace-Loaders.
|
||||
/// Structured errors of the workspace loader.
|
||||
///
|
||||
/// `Cycle.path` ist die Kette der Modul-Namen, in der der Zyklus geschlossen
|
||||
/// wurde — das letzte Element ist der bereits in `visiting` enthaltene Name.
|
||||
/// `Cycle.path` is the chain of module names in which the cycle was
|
||||
/// closed — the last element is the name already present in `visiting`.
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum WorkspaceLoadError {
|
||||
#[error("io error for {path}: {source}")]
|
||||
@@ -65,24 +65,24 @@ pub enum WorkspaceLoadError {
|
||||
ModuleHashMismatch { name: String },
|
||||
}
|
||||
|
||||
/// Hash über die kanonischen Bytes eines kompletten Moduls.
|
||||
/// Hash over the canonical bytes of a complete module.
|
||||
///
|
||||
/// Parallel zu `def_hash`, aber auf Modul-Ebene. Der Workspace-Loader nutzt
|
||||
/// das, um Doppelladungen zu verifizieren; die CLI nutzt das, um pro Modul
|
||||
/// einen stabilen Identifier auszugeben.
|
||||
/// Parallel to `def_hash`, but at module level. The workspace loader
|
||||
/// uses this to verify double-loads; the CLI uses it to emit a stable
|
||||
/// per-module identifier.
|
||||
pub fn module_hash(m: &Module) -> String {
|
||||
let bytes = canonical::to_bytes(m);
|
||||
let h = blake3::hash(&bytes);
|
||||
h.to_hex().as_str()[..16].to_string()
|
||||
}
|
||||
|
||||
/// Lade Eintrittsmodul plus alle transitiv erreichbaren Module.
|
||||
/// Load entry module plus all transitively reachable modules.
|
||||
///
|
||||
/// Algorithmus: DFS über `imports`, mit zwei Mengen:
|
||||
/// - `loaded` (= `modules`-Map): Module, deren Subtree bereits vollständig
|
||||
/// abgearbeitet ist. Beim erneuten Treffen wird nur Hash-Konsistenz geprüft.
|
||||
/// - `visiting`: Stack von Modulen, deren DFS-Abstieg noch läuft. Treffer
|
||||
/// hier = Zyklus.
|
||||
/// Algorithm: DFS over `imports`, with two sets:
|
||||
/// - `loaded` (= `modules` map): modules whose subtree is already fully
|
||||
/// processed. On a re-hit only hash consistency is checked.
|
||||
/// - `visiting`: stack of modules whose DFS descent is still running.
|
||||
/// A hit here = cycle.
|
||||
pub fn load_workspace(entry_path: &Path) -> Result<Workspace, WorkspaceLoadError> {
|
||||
let entry_path = entry_path.to_path_buf();
|
||||
let root_dir = entry_path
|
||||
@@ -90,7 +90,7 @@ pub fn load_workspace(entry_path: &Path) -> Result<Workspace, WorkspaceLoadError
|
||||
.map(Path::to_path_buf)
|
||||
.unwrap_or_else(|| PathBuf::from("."));
|
||||
|
||||
// Eintrittsmodul laden + Konventions-Check Name<->Dateiname.
|
||||
// Load entry module + check name<->filename convention.
|
||||
let entry_module = load_one(&entry_path)?;
|
||||
let expected_entry_name = module_name_from_path(&entry_path);
|
||||
if entry_module.name != expected_entry_name {
|
||||
@@ -130,13 +130,13 @@ fn visit(
|
||||
) -> Result<(), WorkspaceLoadError> {
|
||||
let name = module.name.clone();
|
||||
|
||||
// Schon abgeschlossen geladen? Dann nichts tun. Hash-Konsistenz wird beim
|
||||
// Wieder-Antreffen über Imports geprüft (siehe unten in der Schleife).
|
||||
// Already fully loaded? Then do nothing. Hash consistency is checked
|
||||
// on re-encounter via imports (see below in the loop).
|
||||
if modules.contains_key(&name) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Zyklus: derselbe Name liegt aktuell auf dem DFS-Stack.
|
||||
// Cycle: same name currently on the DFS stack.
|
||||
if visiting_set.contains(&name) {
|
||||
let mut path = visiting.clone();
|
||||
path.push(name);
|
||||
@@ -146,14 +146,14 @@ fn visit(
|
||||
visiting.push(name.clone());
|
||||
visiting_set.insert(name.clone());
|
||||
|
||||
// Imports rekursiv abarbeiten.
|
||||
// Process imports recursively.
|
||||
let imports = module.imports.clone();
|
||||
for imp in &imports {
|
||||
let imp_path = root_dir.join(format!("{}.ail.json", imp.module));
|
||||
|
||||
if let Some(existing) = modules.get(&imp.module) {
|
||||
// Schon vollständig geladen — Hash-Konsistenz prüfen, falls die
|
||||
// Datei auf Platte sich geändert hat.
|
||||
// Already fully loaded — check hash consistency, in case the
|
||||
// file on disk has changed.
|
||||
let on_disk = match load_one(&imp_path) {
|
||||
Ok(m) => m,
|
||||
Err(WorkspaceLoadError::Io { .. }) => continue,
|
||||
@@ -213,11 +213,11 @@ fn load_one(path: &Path) -> Result<Module, WorkspaceLoadError> {
|
||||
|
||||
fn module_name_from_path(p: &Path) -> String {
|
||||
let file = p.file_name().and_then(|s| s.to_str()).unwrap_or("");
|
||||
// Konvention: `<name>.ail.json`.
|
||||
// Convention: `<name>.ail.json`.
|
||||
if let Some(stripped) = file.strip_suffix(".ail.json") {
|
||||
return stripped.to_string();
|
||||
}
|
||||
// Fallback: nur die letzte Extension entfernen.
|
||||
// Fallback: strip only the last extension.
|
||||
Path::new(file)
|
||||
.file_stem()
|
||||
.and_then(|s| s.to_str())
|
||||
@@ -258,9 +258,9 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn loads_example_workspace_happy_path() {
|
||||
// Nutzt die kanonischen Beispiel-Files unter `examples/`. Dieser
|
||||
// Test dokumentiert, dass der Loader sich auf das committete
|
||||
// Workspace-Beispiel verlässt.
|
||||
// Uses the canonical example files under `examples/`. This
|
||||
// test documents that the loader relies on the committed
|
||||
// workspace example.
|
||||
let manifest_dir = env!("CARGO_MANIFEST_DIR");
|
||||
let workspace_root =
|
||||
Path::new(manifest_dir).parent().unwrap().parent().unwrap();
|
||||
|
||||
Reference in New Issue
Block a user