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:
+272
-276
@@ -1,351 +1,347 @@
|
||||
# JOURNAL
|
||||
|
||||
Chronologische Notizen für mich. Nicht jede Änderung; nur Entscheidungen,
|
||||
Hindernisse, Beobachtungen, die zukünftige Iterationen brauchen.
|
||||
Chronological notes for myself. Not every change; only decisions, obstacles,
|
||||
and observations that future iterations will need.
|
||||
|
||||
## 2026-05-07 — Tag 0
|
||||
## 2026-05-07 — Day 0
|
||||
|
||||
- Repo initialisiert. Auftrag in `CLAUDE.md`: LLM-native Sprache, LLVM-Backend.
|
||||
- Designentscheidungen festgehalten in `docs/DESIGN.md`.
|
||||
- Toolchain: `rustc 1.94`, `llvm-config 22.1.3`, `clang` vorhanden.
|
||||
- Entschieden gegen `inkwell` zugunsten LLVM-IR-Text-Emit. Begründung im DESIGN.md.
|
||||
- Workspace-Layout:
|
||||
- `crates/ailang-core` — AST, Type, Hash, JSON-Schema
|
||||
- `crates/ailang-check` — Typchecker (kommt später)
|
||||
- `crates/ailang-codegen` — Lowering + LLVM IR Emit
|
||||
- Repo initialised. Assignment in `CLAUDE.md`: LLM-native language, LLVM backend.
|
||||
- Design decisions captured in `docs/DESIGN.md`.
|
||||
- Toolchain: `rustc 1.94`, `llvm-config 22.1.3`, `clang` available.
|
||||
- Decided against `inkwell` in favour of LLVM IR text emit. Rationale in DESIGN.md.
|
||||
- Workspace layout:
|
||||
- `crates/ailang-core` — AST, type, hash, JSON schema
|
||||
- `crates/ailang-check` — typechecker (comes later)
|
||||
- `crates/ailang-codegen` — lowering + LLVM IR emit
|
||||
- `crates/ail` — CLI
|
||||
- MVP-Ziel: `examples/sum.ail.json` → Binary, das 55 druckt. **Erreicht.**
|
||||
- MVP goal: `examples/sum.ail.json` → binary that prints 55. **Achieved.**
|
||||
|
||||
## 2026-05-07 — Architektur-Review nach MVP
|
||||
## 2026-05-07 — architecture review after the MVP
|
||||
|
||||
Wieder am Pfad? Im Großen ja. Konkrete Beobachtungen:
|
||||
Still on track? Broadly yes. Concrete observations:
|
||||
|
||||
**Was hält:**
|
||||
**What holds:**
|
||||
|
||||
- JSON-AST + canonical form + content-hash sind alle Lego-Steine, auf denen
|
||||
spätere Tools ohne Refactor aufsetzen können (`ail deps`, `ail diff`).
|
||||
- LLVM-IR-Text-Pipeline arbeitet wie geplant. Keine libllvm-Versionsschmerzen.
|
||||
- Effekt-Set ist im Typsystem von Anfang an verbaut. Erweiterbar zu row-poly,
|
||||
ohne den Kern zu ändern.
|
||||
- JSON AST + canonical form + content hash are all lego bricks that later
|
||||
tools can build on without a refactor (`ail deps`, `ail diff`).
|
||||
- The LLVM IR text pipeline works as planned. No libllvm version pain.
|
||||
- The effect set is wired into the type system from the start. Extensible to
|
||||
row-poly without touching the core.
|
||||
|
||||
**Schulden, die Zinsen tragen:**
|
||||
**Debt that accrues interest:**
|
||||
|
||||
1. **`current_block_label_for_phi` ist eine Heuristik** (siehe codegen). Bei
|
||||
verschachtelten `if`-Termen wird sie das falsche Block-Label zurückgeben,
|
||||
weil sie rückwärts den Body scannt. Tickend, weil keine Test-Fälle das
|
||||
bisher triggern. Muss als nächstes fixiert werden, bevor neue Sprachfeatures
|
||||
dazu kommen.
|
||||
2. **Kein typed-AST.** Codegen liest direkt das Quell-AST und verlässt sich
|
||||
darauf, dass der Typchecker vorher lief. Für MVP OK; sobald ADTs oder
|
||||
Closures dazukommen, brauche ich eine getrennte typisierte IR-Stufe (TIR).
|
||||
3. **`hash`-Feld ist nicht im AST.** Aktuell hashen wir das Def-Objekt direkt.
|
||||
Wenn ich Hashes später als Felder mitserialisiere (Caching), muss der Hash
|
||||
das Feld vor der Berechnung ausschließen.
|
||||
1. **`current_block_label_for_phi` is a heuristic** (see codegen). On nested
|
||||
`if` terms it will return the wrong block label, because it scans the body
|
||||
backwards. Ticking, because no test cases trigger it yet. Must be fixed
|
||||
next, before new language features arrive.
|
||||
2. **No typed AST.** Codegen reads the source AST directly and relies on
|
||||
the typechecker having run before. Fine for the MVP; once ADTs or
|
||||
closures arrive, I will need a separate typed IR stage (TIR).
|
||||
3. **The `hash` field is not in the AST.** Right now we hash the def object
|
||||
directly. Once I serialise hashes as fields (caching), the hash will
|
||||
need to exclude that field before computation.
|
||||
|
||||
**Plan Iteration 2 (jetzt):**
|
||||
**Plan iteration 2 (now):**
|
||||
|
||||
1. Block-Label-Tracking sauber machen, verschachteltes-if-Test.
|
||||
2. Strings als Literal + `io/print_str`.
|
||||
3. Hello-world-Beispiel als zweiten E2E-Test.
|
||||
4. CLI: `--json`-Output für maschinelle Konsumenten überall, wo es passt.
|
||||
1. Clean up block-label tracking, with a nested-if test.
|
||||
2. Strings as a literal + `io/print_str`.
|
||||
3. Hello-world example as a second E2E test.
|
||||
4. CLI: `--json` output for machine consumers wherever it fits.
|
||||
|
||||
**Plan Iteration 3:**
|
||||
**Plan iteration 3:**
|
||||
|
||||
ADTs + Pattern Matching. Das ist der nächste große Sprung. Erfordert
|
||||
typisierte IR-Stufe (TIR), weil Pattern Matching zu Decision-Trees lowered
|
||||
wird, was eine andere Form ist als der AST.
|
||||
ADTs + pattern matching. That is the next big jump. Requires a typed IR
|
||||
stage (TIR), because pattern matching lowers into decision trees, which
|
||||
have a different shape from the AST.
|
||||
|
||||
## 2026-05-07 — Iteration 2 fertig
|
||||
## 2026-05-07 — iteration 2 done
|
||||
|
||||
- Block-Label-Tracking robust (verschachtelte `if`s funktionieren). Test
|
||||
`max3_picks_largest` schützt das.
|
||||
- Strings als `Lit::Str { value }`, Type `Str` -> LLVM `ptr`, mit
|
||||
`io/print_str` Effekt-Op. `examples/hello.ail.json` druckt einen String.
|
||||
- CLI: `manifest --json`, `builtins --json` für Tool-Konsumenten.
|
||||
- `ail deps [--of NAME] [--json]` listet Aufruf-Edges. Effekt-Ops sind als
|
||||
`effect:NAME` markiert, damit ein Konsument sie filtern kann.
|
||||
- Block-label tracking is now robust (nested `if`s work). Test
|
||||
`max3_picks_largest` protects it.
|
||||
- Strings as `Lit::Str { value }`, type `Str` -> LLVM `ptr`, with
|
||||
`io/print_str` effect op. `examples/hello.ail.json` prints a string.
|
||||
- CLI: `manifest --json`, `builtins --json` for tool consumers.
|
||||
- `ail deps [--of NAME] [--json]` lists call edges. Effect ops are tagged
|
||||
`effect:NAME` so a consumer can filter them.
|
||||
|
||||
**Architektur-Check:** Keine strukturellen Abweichungen. Codegen liest noch
|
||||
direkt das Quell-AST (TIR-Stufe wird mit ADTs in Iteration 3 nötig).
|
||||
**Architecture check:** no structural deviations. Codegen still reads the
|
||||
source AST directly (a TIR stage will become necessary with ADTs in
|
||||
iteration 3).
|
||||
|
||||
## 2026-05-07 — Iteration 3 fertig: ADTs
|
||||
## 2026-05-07 — iteration 3 done: ADTs
|
||||
|
||||
- TypeDef im AST mit Ctors. Ein Ctor hat `name` und `fields: [Type...]`.
|
||||
- Term::Ctor (Konstruktion) und Term::Match (Pattern Matching).
|
||||
- Patterns: `Wild`, `Var`, `Lit`, `Ctor { ctor, fields }`. Im MVP sind nested
|
||||
Ctor-Patterns NICHT erlaubt — Sub-Patterns müssen `Var` oder `Wild` sein.
|
||||
- Typchecker mit Type-Registry und `ctor_index` (ctor-name → ADT). Im Match
|
||||
wird Exhaustiveness gegen die volle Konstruktormenge geprüft. Negativ-Test
|
||||
schützt das.
|
||||
- Codegen: Boxed-Heap-Layout. Pro Ctor-Anwendung `malloc(8 + 8*n)` Bytes;
|
||||
Tag in offset 0, Felder ab offset 8 (8-Byte-Slots, native typed
|
||||
load/store). Match: load tag + switch + arm-blocks + phi am Join.
|
||||
- `examples/list.ail.json` (Cons/Nil-Liste, sum_list über match) liefert 42.
|
||||
- TypeDef in the AST with ctors. A ctor has `name` and `fields: [Type...]`.
|
||||
- Term::Ctor (construction) and Term::Match (pattern matching).
|
||||
- Patterns: `Wild`, `Var`, `Lit`, `Ctor { ctor, fields }`. In the MVP,
|
||||
nested ctor patterns are NOT allowed — sub-patterns must be `Var` or
|
||||
`Wild`.
|
||||
- Typechecker with a type registry and `ctor_index` (ctor name → ADT). In
|
||||
Match, exhaustiveness is checked against the full constructor set. A
|
||||
negative test protects this.
|
||||
- Codegen: boxed heap layout. Per ctor application, `malloc(8 + 8*n)`
|
||||
bytes; tag at offset 0, fields from offset 8 (8-byte slots, native typed
|
||||
load/store). Match: load tag + switch + arm blocks + phi at the join.
|
||||
- `examples/list.ail.json` (Cons/Nil list, sum_list via match) returns 42.
|
||||
|
||||
**Erstaunlich problemlos.** Die Architekturentscheidungen aus Tag 0 haben
|
||||
sich ausgezahlt: opaque ptr in LLVM 22 macht Boxed-Layout fast ohne
|
||||
Glue-Code möglich; das Effekt-Tracking blieb von ADTs unberührt; der
|
||||
JSON-AST nimmt neue Knoten-Typen sauber auf.
|
||||
**Surprisingly painless.** The architecture decisions from day 0 paid off:
|
||||
opaque ptr in LLVM 22 makes the boxed layout almost glue-free; effect
|
||||
tracking was untouched by ADTs; the JSON AST takes new node types
|
||||
cleanly.
|
||||
|
||||
**Gewachsene Schulden:**
|
||||
**Debt accrued:**
|
||||
|
||||
1. **Codegen liest immer noch direkt den Quell-AST.** Die Versuchung war
|
||||
stark, ohne TIR weiterzumachen — und hat funktioniert, weil meine
|
||||
Match-Restriktionen flach sind (keine nested Patterns). Sobald nested
|
||||
Patterns kommen, braucht es ein Decision-Tree-Lowering, das ohne TIR
|
||||
nicht sauber wird. Schulden anerkannt; nicht jetzt fällig.
|
||||
2. **Kein GC.** Heap leakt. Akzeptabel für Demo-Programme; muss vor jedem
|
||||
längerläufigen Programm angegangen werden. Optionen für Phase 4:
|
||||
Refcount, Boehm-GC-Linkage, Region-Inference.
|
||||
3. **Pretty-Printer für ADT-Werte zur Laufzeit fehlt.** `io/print_int`
|
||||
reicht für Demos, aber ein generisches `show :: a -> Str` für ADTs
|
||||
wäre wertvoll. Erfordert dispatch über tag — machbar, aber nicht jetzt.
|
||||
1. **Codegen still reads the source AST directly.** The temptation to push
|
||||
on without TIR was strong — and worked, because my Match restrictions
|
||||
are flat (no nested patterns). Once nested patterns arrive, decision-
|
||||
tree lowering will not stay clean without TIR. Debt acknowledged; not
|
||||
due now.
|
||||
2. **No GC.** The heap leaks. Acceptable for demo programs; must be
|
||||
addressed before any longer-running program. Options for Phase 4:
|
||||
refcount, Boehm-GC linkage, region inference.
|
||||
3. **No runtime pretty-printer for ADT values.** `io/print_int` is enough
|
||||
for demos, but a generic `show :: a -> Str` for ADTs would be valuable.
|
||||
Requires dispatch over the tag — feasible, but not now.
|
||||
|
||||
**Plan Iteration 4:**
|
||||
**Plan iteration 4:**
|
||||
|
||||
Die nächsten Schritte sind weniger eindeutig. Drei Kandidaten in
|
||||
Prioritätsreihenfolge:
|
||||
The next steps are less obvious. Three candidates in priority order:
|
||||
|
||||
1. **Modulsystem (Imports).** Aktuell ist alles in einem Modul. Mit
|
||||
mehreren Modulen + Cross-Module-Hashing wird die Sprache erst
|
||||
praktikabel für mehrere Defs.
|
||||
2. **Strukturierte Fehlerausgabe (`ail check --json`).** Damit Tools auf
|
||||
Typfehler reagieren können, ohne Text zu parsen.
|
||||
3. **Closures / höherwertige Funktionen.** Erfordert Closure-Konvertierung
|
||||
und ist ein größerer Schritt.
|
||||
1. **Module system (imports).** Right now everything is in a single module.
|
||||
With multiple modules + cross-module hashing the language only becomes
|
||||
practical for several defs.
|
||||
2. **Structured error output (`ail check --json`).** So tools can react to
|
||||
type errors without parsing text.
|
||||
3. **Closures / higher-order functions.** Requires closure conversion and
|
||||
is a bigger step.
|
||||
|
||||
Iteration 4 wird (1) + (2) — beides macht das LLM-Tooling stärker und
|
||||
hat moderates Risiko.
|
||||
Iteration 4 will be (1) + (2) — both strengthen the LLM tooling and have
|
||||
moderate risk.
|
||||
|
||||
## 2026-05-07 — Workflow-Wechsel: Orchestrator + Agent-Repo
|
||||
## 2026-05-07 — workflow change: orchestrator + agent repo
|
||||
|
||||
Auf Anregung des Auftraggebers Wechsel auf **Orchestrator-Modus**: ich
|
||||
delegiere abgrenzte Implementierungs-Brocken an Sub-Agenten und behalte
|
||||
nur Architektur-Entscheidungen, Reviews und Commit-Disziplin. Vier
|
||||
spezialisierte Agenten formuliert: implementer, architect, tester,
|
||||
debugger.
|
||||
At the user's suggestion, switching to **orchestrator mode**: I delegate
|
||||
clearly bounded implementation chunks to sub-agents and keep only
|
||||
architecture decisions, reviews, and commit discipline. Four specialised
|
||||
agents drafted: implementer, architect, tester, debugger.
|
||||
|
||||
**Wichtige Korrektur:** Auftraggeber hat verlangt, dass die Agenten nicht
|
||||
in `.claude/agents/` versteckt liegen, sondern als sichtbarer Bestandteil
|
||||
des Projekts unter `agents/` versioniert werden. DESIGN.md hat einen neuen
|
||||
Abschnitt "Projekt-Ökosystem" bekommen, der das festhält: AILang ist nicht
|
||||
nur eine Sprache, sondern Sprachkern + CLI + Examples + Agents + Doku +
|
||||
Tests, alle gleichwertig.
|
||||
**Important correction:** the user required the agents not to be hidden in
|
||||
`.claude/agents/`, but versioned as a visible part of the project under
|
||||
`agents/`. DESIGN.md gained a new section "Project ecosystem", which
|
||||
records this: AILang is not just a language, but language core + CLI +
|
||||
examples + agents + docs + tests, all of equal weight.
|
||||
|
||||
Aufruf-Schema: System-Prompt-Body aus `agents/<name>.md` als Präfix vor
|
||||
die konkrete Aufgabe + an `general-purpose`-Agent. Funktional identisch
|
||||
zum Subagent-Loading aus `.claude/agents/`, aber sichtbar im Repo.
|
||||
Invocation scheme: the system-prompt body from `agents/<name>.md` as a
|
||||
prefix before the concrete task + sent to the `general-purpose` agent.
|
||||
Functionally identical to subagent loading from `.claude/agents/`, but
|
||||
visible in the repo.
|
||||
|
||||
**Plan Iteration 4 (überarbeitet):**
|
||||
**Plan iteration 4 (revised):**
|
||||
|
||||
Modulsystem ist aufwändiger als erwartet (Cross-Module-Hashing,
|
||||
Import-Resolution). Erst die kleineren Tooling-Wins ziehen, dann das
|
||||
Modulsystem als Iteration 5:
|
||||
The module system is more involved than expected (cross-module hashing,
|
||||
import resolution). First the smaller tooling wins, then the module system
|
||||
as iteration 5:
|
||||
|
||||
1. **Strukturierte Fehlerausgabe** (`ail check --json` mit Diagnostic-
|
||||
Struct, stabile Codes wie `unbound-var`, `type-mismatch`).
|
||||
2. **`ail diff <a> <b>`** — semantischer Modul-Diff via Hash-Vergleich
|
||||
pro Def.
|
||||
3. **IR-Snapshot-Tests** — Regressions-Schutz für die Codegen-Pipeline.
|
||||
1. **Structured error output** (`ail check --json` with a Diagnostic
|
||||
struct, stable codes like `unbound-var`, `type-mismatch`).
|
||||
2. **`ail diff <a> <b>`** — semantic module diff via per-def hash
|
||||
comparison.
|
||||
3. **IR snapshot tests** — regression protection for the codegen pipeline.
|
||||
|
||||
## 2026-05-07 — Iteration 4 fertig: LLM-Tooling-Konsolidierung
|
||||
## 2026-05-07 — iteration 4 done: LLM tooling consolidation
|
||||
|
||||
Drei Sub-Commits, jeder von einem `ailang-implementer`-Aufruf erzeugt
|
||||
und stichprobenartig vom Orchestrator verifiziert:
|
||||
Three sub-commits, each produced by an `ailang-implementer` invocation
|
||||
and spot-checked by the orchestrator:
|
||||
|
||||
- `93fe723` Iter 4a: `ail check --json` mit `Diagnostic`-Struct
|
||||
(`severity`, `code`, `message`, `def`, `ctx`). Stabile Codes:
|
||||
- `93fe723` Iter 4a: `ail check --json` with a `Diagnostic` struct
|
||||
(`severity`, `code`, `message`, `def`, `ctx`). Stable codes:
|
||||
`unbound-var`, `type-mismatch`, `arity-mismatch`,
|
||||
`non-exhaustive-match`, `unknown-ctor`, `unknown-ctor-in-pattern`,
|
||||
`nested-ctor-pattern-not-allowed`, `duplicate-def`,
|
||||
`unknown-effect-op`, `unknown-type`, `schema-mismatch`. API:
|
||||
`check_module(&Module) -> Vec<Diagnostic>`.
|
||||
- `c652b12` Iter 4b: `ail diff <a> <b> [--json]` als struktureller Top-
|
||||
Level-Def-Diff via BLAKE3-Hash. Vier Kategorien (added/removed/
|
||||
changed/unchanged), alphabetisch sortiert, Exit-Code 1 bei Diff.
|
||||
- `74a2005` Iter 4c: IR-Snapshot-Tests in
|
||||
- `c652b12` Iter 4b: `ail diff <a> <b> [--json]` as a structural top-level
|
||||
def diff via BLAKE3 hash. Four categories (added/removed/changed/
|
||||
unchanged), sorted alphabetically, exit code 1 on diff.
|
||||
- `74a2005` Iter 4c: IR snapshot tests in
|
||||
`crates/ail/tests/snapshots/{sum,max3,hello,list}.ll`.
|
||||
Normalisierung von `target triple`. Update via `UPDATE_SNAPSHOTS=1
|
||||
cargo test ir_snapshot_`. Mismatch erzeugt `.actual`-Datei.
|
||||
Normalisation of `target triple`. Update via `UPDATE_SNAPSHOTS=1
|
||||
cargo test ir_snapshot_`. Mismatch produces an `.actual` file.
|
||||
|
||||
Test-Stand: 28 (vorher 19). 7 E2E + 4 IR-Snapshot + 9 ailang-check + 1
|
||||
Test count: 28 (previously 19). 7 E2E + 4 IR snapshot + 9 ailang-check + 1
|
||||
ailang-codegen + 7 ailang-core.
|
||||
|
||||
**Erledigt aus dem Schulden-Register:**
|
||||
**Closed from the debt register:**
|
||||
|
||||
- Block-Tracking im Codegen ist seit Iter 2 kein Heuristik-Risiko mehr;
|
||||
die explizite `current_block: String`-Spur wurde durch Iter 4c jetzt
|
||||
zusätzlich von Snapshot-Tests gegen Regression geschützt. Schuld
|
||||
geschlossen.
|
||||
- Block tracking in codegen has not been a heuristic risk since Iter 2;
|
||||
the explicit `current_block: String` track is now additionally
|
||||
protected against regression by Iter 4c snapshot tests. Debt closed.
|
||||
|
||||
**Neue / präzisierte Schulden:**
|
||||
**New / sharpened debt:**
|
||||
|
||||
1. `check_module` ist **single-shot** — der erste Fehler bricht ab, kein
|
||||
Multi-Diagnose-Sammeln. Spec war so, aber das Format suggeriert
|
||||
Vec-Semantik. Echter Multi-Diagnose-Refactor wird billiger, sobald
|
||||
TIR existiert (zentraler Fehler-Akku durch separate Stufe). Nicht
|
||||
jetzt fällig.
|
||||
2. `source_filename` im IR ist hartkodiert auf `"<module>.ail"`. Solange
|
||||
es nur ein Top-Level-Modul gibt, plattform-stabil. Mit Iter 5
|
||||
(Modulsystem + Imports) wird der Pfad relevant — dort gleich beim
|
||||
Bau pfad-unabhängig halten, sonst kippen die Snapshots.
|
||||
1. `check_module` is **single-shot** — the first error aborts, no
|
||||
multi-diagnostic gathering. The spec was that way, but the format
|
||||
suggests Vec semantics. A real multi-diagnostic refactor will be
|
||||
cheaper once TIR exists (a central error accumulator via a separate
|
||||
stage). Not due now.
|
||||
2. `source_filename` in the IR is hard-coded to `"<module>.ail"`. As long
|
||||
as there is only one top-level module, that is platform-stable. With
|
||||
Iter 5 (module system + imports) the path becomes relevant — keep it
|
||||
path-independent at construction time, otherwise the snapshots will
|
||||
tip over.
|
||||
|
||||
**Plan Iteration 5:** Modulsystem mit Imports. Cross-Module-Hashing,
|
||||
Import-Auflösung, mehrere `.ail.json`-Files in einem Build. Multi-
|
||||
Diagnose-Refactor erst danach.
|
||||
**Plan iteration 5:** module system with imports. Cross-module hashing,
|
||||
import resolution, multiple `.ail.json` files in one build. The multi-
|
||||
diagnostic refactor only after that.
|
||||
|
||||
Sub-Schritte:
|
||||
Sub-steps:
|
||||
|
||||
- **5a — Workspace-Loader.** `ailang_core::Workspace { modules:
|
||||
BTreeMap<String, Module> }` plus `load_workspace(entry: &Path)`, das
|
||||
`imports` rekursiv vom Eintrittsmodul folgt. Konvention: `import {
|
||||
module: "foo" }` löst auf `<dir>/foo.ail.json` neben dem Entry. Zyklus-
|
||||
Erkennung. CLI: bestehende Subkommandos arbeiten weiter auf einzelnem
|
||||
Modul; ein neues `ail workspace <entry>` listet alle erreichbaren
|
||||
Module mit Hash. Tests: zwei kleine Beispielmodule mit
|
||||
Import-Beziehung; Zyklus-Test.
|
||||
- **5b — Cross-Module-Typcheck.** Typchecker bekommt `&Workspace`
|
||||
statt `&Module`. Imports werden im Env als Namespace eingehängt
|
||||
(`alias.def` oder bei fehlendem Alias `module.def`). Neue Diagnostic-
|
||||
Codes: `unknown-module`, `unknown-import`, `import-cycle`,
|
||||
`ambiguous-name`. Tests pro Code.
|
||||
- **5c — Cross-Module-Codegen.** Emitter erzeugt IR für alle Module im
|
||||
Workspace, prefix-mangelt mit `@ail_<module>_<def>`. E2E-Test:
|
||||
Programm, das eine Funktion aus Modul B in Modul A nutzt, liefert
|
||||
korrektes Ergebnis im Binary.
|
||||
- **5d — Tooling-Anpassungen.** `manifest`, `describe`, `deps`, `diff`
|
||||
bekommen einen `--workspace`-Modus (rekursiv). Single-Modus bleibt
|
||||
Default für Rückwärtskompatibilität.
|
||||
- **5a — workspace loader.** `ailang_core::Workspace { modules:
|
||||
BTreeMap<String, Module> }` plus `load_workspace(entry: &Path)`, which
|
||||
follows `imports` recursively from the entry module. Convention:
|
||||
`import { module: "foo" }` resolves to `<dir>/foo.ail.json` next to the
|
||||
entry. Cycle detection. CLI: existing subcommands keep working on a
|
||||
single module; a new `ail workspace <entry>` lists all reachable
|
||||
modules with hash. Tests: two small example modules with an import
|
||||
relation; cycle test.
|
||||
- **5b — cross-module typecheck.** The typechecker takes `&Workspace`
|
||||
instead of `&Module`. Imports are mounted in the env as a namespace
|
||||
(`alias.def` or, with no alias, `module.def`). New diagnostic codes:
|
||||
`unknown-module`, `unknown-import`, `import-cycle`, `ambiguous-name`.
|
||||
Tests per code.
|
||||
- **5c — cross-module codegen.** The emitter produces IR for all modules
|
||||
in the workspace, prefix-mangled with `@ail_<module>_<def>`. E2E test:
|
||||
a program that uses a function from module B in module A returns the
|
||||
correct result in the binary.
|
||||
- **5d — tooling adjustments.** `manifest`, `describe`, `deps`, `diff`
|
||||
gain a `--workspace` mode (recursive). The single mode stays the
|
||||
default for backwards compatibility.
|
||||
|
||||
Während Iter 5 gleich beim Bau **`source_filename` pfad-unabhängig
|
||||
halten** (nur Modulname, kein Verzeichnis-Prefix), sonst kippen die
|
||||
IR-Snapshots.
|
||||
During Iter 5, at construction time **keep `source_filename`
|
||||
path-independent** (module name only, no directory prefix), otherwise
|
||||
the IR snapshots will tip over.
|
||||
|
||||
## 2026-05-07 — Iter 5b fertig: Cross-Module-Typcheck
|
||||
## 2026-05-07 — Iter 5b done: cross-module typecheck
|
||||
|
||||
- `check_workspace(&Workspace) -> Vec<Diagnostic>` als Top-Level-API.
|
||||
`check_module` bleibt erhalten und hebt das Modul intern in einen
|
||||
Trivial-Workspace.
|
||||
- Konvention für qualifizierte Verweise (in DESIGN.md festgehalten):
|
||||
`Term::Var { name }` mit genau einem Punkt = `<prefix>.<def>`. Prefix
|
||||
ist Import-Alias oder Modulname. Kein neuer AST-Knoten, keine
|
||||
umbenannten Felder ⇒ Hashes bleiben stabil; alle `ir_snapshot_*`
|
||||
weiterhin grün.
|
||||
- Drei neue Diagnostic-Codes: `unknown-module`,
|
||||
`unknown-import`, `invalid-def-name` (mit `ctx.reason: "contains-dot"`).
|
||||
- CLI: `ail check <entry>` lädt jetzt **immer** über `load_workspace`.
|
||||
Workspace-Lade-Fehler werden im JSON-Modus zu strukturierten
|
||||
Diagnostics mit Codes `module-not-found`, `module-cycle`,
|
||||
`module-name-mismatch`, `module-hash-mismatch`, `schema-mismatch`.
|
||||
`ail build` und `ail emit-ir` bleiben pro Einzelmodul (Codegen
|
||||
cross-module ist 5c).
|
||||
- Beispiele: `ws_main.ail.json` ruft jetzt `ws_lib.add` auf
|
||||
(beobachtbar). Neu: `ws_broken.ail.json` (`unknown-import`),
|
||||
- `check_workspace(&Workspace) -> Vec<Diagnostic>` as the top-level API.
|
||||
`check_module` is preserved and internally lifts the module into a
|
||||
trivial workspace.
|
||||
- Convention for qualified references (recorded in DESIGN.md):
|
||||
`Term::Var { name }` with exactly one dot = `<prefix>.<def>`. Prefix is
|
||||
an import alias or module name. No new AST node, no renamed fields ⇒
|
||||
hashes stay stable; all `ir_snapshot_*` still green.
|
||||
- Three new diagnostic codes: `unknown-module`, `unknown-import`,
|
||||
`invalid-def-name` (with `ctx.reason: "contains-dot"`).
|
||||
- CLI: `ail check <entry>` now **always** loads via `load_workspace`.
|
||||
Workspace load failures become structured diagnostics in JSON mode with
|
||||
codes `module-not-found`, `module-cycle`, `module-name-mismatch`,
|
||||
`module-hash-mismatch`, `schema-mismatch`. `ail build` and
|
||||
`ail emit-ir` stay per single module (cross-module codegen is 5c).
|
||||
- Examples: `ws_main.ail.json` now calls `ws_lib.add` (observable). New:
|
||||
`ws_broken.ail.json` (`unknown-import`),
|
||||
`ws_unknown_module.ail.json` (`unknown-module`).
|
||||
- Tests: 37 grün (vorher 32). 4 neue Workspace-Integrationstests in
|
||||
`crates/ailang-check/tests/workspace.rs`, ein neuer e2e-Test
|
||||
- Tests: 37 green (previously 32). 4 new workspace integration tests in
|
||||
`crates/ailang-check/tests/workspace.rs`, one new e2e test
|
||||
`check_workspace_resolves_import`.
|
||||
- Schulden: Single-Shot-Diagnostics weiterhin (Multi-Diagnose nach 5c).
|
||||
Punkt-Konvention deckt nur einen Punkt ab — verschachtelte Modulpfade
|
||||
(`a.b.c`) gibt es nicht; das wäre erst mit Hierarchie-Modulen ein
|
||||
Thema und fällt aktuell als `unbound-var` durch.
|
||||
- Debt: single-shot diagnostics still in place (multi-diagnostic after
|
||||
5c). The dot convention covers exactly one dot — nested module paths
|
||||
(`a.b.c`) do not exist; that would only be a topic with hierarchical
|
||||
modules and currently falls through as `unbound-var`.
|
||||
|
||||
## 2026-05-07 — Iter 5c fertig: Cross-Module-Codegen
|
||||
## 2026-05-07 — Iter 5c done: cross-module codegen
|
||||
|
||||
- **Mangling-Bruch (mit Bedacht).** Alle AILang-Funktionen heißen jetzt
|
||||
`@ail_<modul>_<def>`, auch in Single-Modul-Programmen. Die alte Form
|
||||
`@ail_<def>` ist weg. Strings/Const-Globals analog
|
||||
(`@.str_<modul>_<hint>_<idx>`, `@ail_<modul>_<const>`). Eintrittspunkt
|
||||
bleibt `main` als C-ABI: ein `define i32 @main()`-Trampoline ruft
|
||||
`@ail_<entry-modul>_main()`. Fehlt im Eintrittsmodul ein
|
||||
`main : () -> Unit !IO`, fällt der Build mit `MissingEntryMain`.
|
||||
- **Workspace-Lowering.** Neue Top-Level-API
|
||||
- **Mangling break (deliberate).** All AILang functions are now called
|
||||
`@ail_<module>_<def>`, even in single-module programs. The old form
|
||||
`@ail_<def>` is gone. Strings/const globals analogously
|
||||
(`@.str_<module>_<hint>_<idx>`, `@ail_<module>_<const>`). The entry
|
||||
point stays `main` as C ABI: a `define i32 @main()` trampoline calls
|
||||
`@ail_<entry-module>_main()`. If the entry module has no
|
||||
`main : () -> Unit !IO`, the build fails with `MissingEntryMain`.
|
||||
- **Workspace lowering.** New top-level API
|
||||
`ailang_codegen::lower_workspace(ws: &Workspace) -> Result<String>`
|
||||
produziert eine einzelne `.ll` für den ganzen Workspace. Module
|
||||
alphabetisch (BTreeMap-Order); Defs in AST-Reihenfolge. Cross-Module-
|
||||
Calls werden im Codegen über die Import-Map des aufrufenden Moduls
|
||||
aufgelöst — gleiche Logik wie im Typchecker, lokal dupliziert mit
|
||||
Verweis (kein gemeinsames Helper-Modul, weil die Typ-Welten
|
||||
unterschiedlich sind: Typchecker hantiert mit `Type`, Codegen mit
|
||||
`FnSig` aus llvm-Typen).
|
||||
- **CLI.** `ail build` und `ail emit-ir` laden jetzt immer den Workspace
|
||||
und checken/lowern ihn vollständig. Single-Modul-Programme funktionieren
|
||||
weiter (Trivial-Workspace mit einem Modul). `emit_ir(m)` bleibt im
|
||||
Codegen-Crate als Bequemlichkeits-API erhalten und wrapt intern in
|
||||
einen Trivial-Workspace.
|
||||
- **Snapshots regeneriert.** `sum.ll`, `max3.ll`, `hello.ll`, `list.ll`
|
||||
zeigen das neue Mangling. Neuer `ws_main.ll`-Snapshot dokumentiert den
|
||||
Cross-Module-Build: `@ail_ws_main_main` ruft `@ail_ws_lib_add` auf.
|
||||
- **Tests.** 40 grün (vorher 37). Neu: `workspace_build_runs_imported_fn`
|
||||
(e2e: druckt 5), `ir_snapshot_ws_main`, `missing_entry_main_is_error`
|
||||
(codegen-unit). Bestehende Verhaltens-Tests
|
||||
produces a single `.ll` for the whole workspace. Modules in
|
||||
alphabetical order (BTreeMap order); defs in AST order. Cross-module
|
||||
calls are resolved in codegen via the import map of the calling module
|
||||
— same logic as in the typechecker, locally duplicated with a
|
||||
cross-reference (no shared helper module, because the type worlds
|
||||
differ: the typechecker handles `Type`, codegen handles `FnSig` from
|
||||
llvm types).
|
||||
- **CLI.** `ail build` and `ail emit-ir` now always load the workspace
|
||||
and check/lower it fully. Single-module programs keep working
|
||||
(trivial workspace with one module). `emit_ir(m)` stays in the codegen
|
||||
crate as a convenience API and internally wraps into a trivial
|
||||
workspace.
|
||||
- **Snapshots regenerated.** `sum.ll`, `max3.ll`, `hello.ll`, `list.ll`
|
||||
show the new mangling. New `ws_main.ll` snapshot documents the
|
||||
cross-module build: `@ail_ws_main_main` calls `@ail_ws_lib_add`.
|
||||
- **Tests.** 40 green (previously 37). New: `workspace_build_runs_imported_fn`
|
||||
(e2e: prints 5), `ir_snapshot_ws_main`, `missing_entry_main_is_error`
|
||||
(codegen unit). Existing behaviour tests
|
||||
(`sum_1_to_10_is_55`, `max3_picks_largest`, `hello_world_str_lit`,
|
||||
`list_sum_via_match`) bleiben grün — Verhalten unverändert, nur das
|
||||
Mangling ist neu.
|
||||
`list_sum_via_match`) stay green — behaviour unchanged, only the
|
||||
mangling is new.
|
||||
|
||||
**Schulden geschlossen:**
|
||||
**Debt closed:**
|
||||
|
||||
- **#19 (`source_filename` härten).** In der Workspace-Welt ist
|
||||
`source_filename` jetzt einheitlich `<entry-modul>.ail`, einmal pro
|
||||
Workspace. Der bisherige hartkodierte Pfad-Punkt entfällt damit.
|
||||
- **#19 (`source_filename` hardening).** In the workspace world,
|
||||
`source_filename` is now uniformly `<entry-module>.ail`, once per
|
||||
workspace. The previous hard-coded path dot is gone with it.
|
||||
|
||||
**Stand:** Modulsystem ist Ende-zu-Ende geschlossen — Loader + Typcheck
|
||||
+ Codegen + Build sehen den Workspace als kohärente Einheit. Multi-
|
||||
Diagnose-Refactor und ggf. ADT-Cross-Module bleiben für später.
|
||||
**State:** the module system is closed end to end — loader + typecheck +
|
||||
codegen + build see the workspace as a coherent unit. The multi-
|
||||
diagnostic refactor and possibly cross-module ADTs remain for later.
|
||||
|
||||
## 2026-05-07 — Iter 5d fertig: Tooling auf Workspace ausgedehnt
|
||||
## 2026-05-07 — Iter 5d done: tooling extended to the workspace
|
||||
|
||||
- `ail manifest|describe|deps|diff <entry> --workspace` arbeiten jetzt
|
||||
über alle Module des Workspaces. Default ohne Flag bleibt single-modul
|
||||
für Rückwärtskompatibilität. Manifest sortiert nach `(modul, name)`,
|
||||
describe akzeptiert Punktnotation `ws_lib.add`, deps emittiert
|
||||
`{from_module, from_def, to_module, to_def}`-Edges, diff vergleicht
|
||||
workspace-übergreifend mit added/removed/changed/unchanged_modules
|
||||
und nested Sub-Diff pro changed_module.
|
||||
- Refactor: `diff_def_lists` ist die einzige Quelle der 4-Kategorie-
|
||||
Logik; Single- und Workspace-Diff teilen sie.
|
||||
- Tests: 44 grün (vorher 40). Neu: `manifest_workspace_lists_all_defs`,
|
||||
- `ail manifest|describe|deps|diff <entry> --workspace` now operate
|
||||
across all modules of the workspace. The default without the flag stays
|
||||
single-module for backwards compatibility. Manifest sorts by
|
||||
`(module, name)`, describe accepts dotted notation `ws_lib.add`, deps
|
||||
emits `{from_module, from_def, to_module, to_def}` edges, diff compares
|
||||
workspace-wide with added/removed/changed/unchanged_modules and a
|
||||
nested sub-diff per changed_module.
|
||||
- Refactor: `diff_def_lists` is the single source of the four-category
|
||||
logic; single and workspace diff share it.
|
||||
- Tests: 44 green (previously 40). New: `manifest_workspace_lists_all_defs`,
|
||||
`describe_workspace_resolves_qualified_name`,
|
||||
`deps_workspace_includes_cross_module`, `diff_workspace_added_module`.
|
||||
|
||||
**Beobachtung (Schuld):** `deps` filtert Builtins/Locals/Funktions-
|
||||
parameter nicht. Im Workspace-Modus wird das deutlicher als im Single-
|
||||
Modus — `ws_lib.add` listet Edges auf `ws_lib.+` (Builtin) und
|
||||
`ws_lib.a`/`ws_lib.b` (Funktionsparameter). Bekanntes Vorbestand-
|
||||
Problem aus Iter 2; Task #22 im Backlog.
|
||||
**Observation (debt):** `deps` does not filter builtins/locals/function
|
||||
parameters. In workspace mode that becomes more visible than in single
|
||||
mode — `ws_lib.add` lists edges to `ws_lib.+` (builtin) and
|
||||
`ws_lib.a`/`ws_lib.b` (function parameters). A known pre-existing
|
||||
issue from Iter 2; Task #22 in the backlog.
|
||||
|
||||
## 2026-05-07 — Architektur-Review nach Iter 5
|
||||
## 2026-05-07 — architecture review after Iter 5
|
||||
|
||||
Architekt-Agent gerufen. Befunde:
|
||||
Architect agent invoked. Findings:
|
||||
|
||||
1. **Mangling-Konsistenz hält.** `@ail_<modul>_<def>` durchgängig in
|
||||
Funktionen, Konstanten, String-Globals, Cross-Module-Calls.
|
||||
Trampoline korrekt. ADT-Konstruktoren sind absichtlich symbol-frei
|
||||
(inline malloc).
|
||||
2. **Modul-Hashes bitidentisch seit Iter 4.** Iter-5c-Snapshot-
|
||||
Regenerierung war Codegen-Output-Änderung, nicht Hash-Bruch.
|
||||
3. **Drift, sofort fällig:**
|
||||
- DESIGN.md sagt `define i64 @main()`, der Codegen emittiert
|
||||
`define i32 @main()` (siehe `sum.ll:35`).
|
||||
- Stringschema-Notation in DESIGN.md verkürzt
|
||||
(`@.str_<modul>_<idx>` statt `@.str_<modul>_<hint>_<idx>`).
|
||||
4. **Schulden, die Zinsen tragen:** `deps`-Builtin-Leck (Task #22) ist
|
||||
im Workspace-Modus zu einer Falschaussage geworden — vor dem
|
||||
nächsten großen Sprung schließen.
|
||||
1. **Mangling consistency holds.** `@ail_<module>_<def>` is consistent
|
||||
across functions, constants, string globals, and cross-module calls.
|
||||
The trampoline is correct. ADT constructors are deliberately
|
||||
symbol-free (inline malloc).
|
||||
2. **Module hashes bit-identical since Iter 4.** The Iter 5c snapshot
|
||||
regeneration was a codegen-output change, not a hash break.
|
||||
3. **Drift, due now:**
|
||||
- DESIGN.md says `define i64 @main()`, codegen emits
|
||||
`define i32 @main()` (see `sum.ll:35`).
|
||||
- String-schema notation in DESIGN.md was shortened
|
||||
(`@.str_<module>_<idx>` instead of `@.str_<module>_<hint>_<idx>`).
|
||||
4. **Debt that accrues interest:** the `deps` builtin leak (Task #22) has
|
||||
become a falsehood in workspace mode — close it before the next big
|
||||
jump.
|
||||
|
||||
**Plan Iteration 6 — Aufräumarbeiten:**
|
||||
**Plan iteration 6 — clean-up:**
|
||||
|
||||
1. **DESIGN.md-Drift fixen.** Mangling-Schema-Block aktualisieren,
|
||||
`@main`-Signatur korrigieren, String-Globals präzise notieren.
|
||||
2. **`deps`-Härtung (#22).** Pro Workspace eine Top-Level-Def-Tabelle
|
||||
bauen; Edges, deren Ziel kein Top-Level-Symbol ist, filtern oder
|
||||
als separate `builtin:`/`local:`-Kategorie ausgeben.
|
||||
Funktionsparameter via Lexikalisches Scope-Tracking aus walk_term.
|
||||
3. **Multi-Diagnose-Refactor (#20).** `check_workspace` akkumuliert
|
||||
`Vec<Diagnostic>` über alle Defs, statt beim ersten Error zu
|
||||
shortcuten. Intra-Def darf weiter shortcuten — der Wert ist
|
||||
"alle defektiven Defs auf einmal sehen", nicht "alle defektiven
|
||||
Subterme einer Def sehen".
|
||||
1. **Fix DESIGN.md drift.** Update the mangling-scheme block, correct the
|
||||
`@main` signature, and note the string globals precisely.
|
||||
2. **`deps` hardening (#22).** Build a top-level def table per workspace;
|
||||
filter edges whose target is not a top-level symbol, or emit them as
|
||||
separate `builtin:`/`local:` categories. Function parameters via
|
||||
lexical scope tracking from walk_term.
|
||||
3. **Multi-diagnostic refactor (#20).** `check_workspace` accumulates
|
||||
`Vec<Diagnostic>` across all defs instead of short-circuiting on the
|
||||
first error. Intra-def may still short-circuit — the value is "see
|
||||
all broken defs at once", not "see all broken sub-terms of one def".
|
||||
|
||||
Reihenfolge: 1 zuerst (Doku-Trivialität), dann 2 vor 3 (deps ist eine
|
||||
Tooling-Wahrheit-Reparatur, Multi-Diag eine Strukturerweiterung).
|
||||
Order: 1 first (doc triviality), then 2 before 3 (deps is a tooling-
|
||||
truth fix, multi-diag is a structural extension).
|
||||
|
||||
Reference in New Issue
Block a user