Files
RustAst/docs/Analysis_Environment.md
Brummel 35f5ea0db3 Refactor Binder and Environment, remove fixed_scope_idx
The `fixed_scope_idx` field has been removed from `Binder` and
`Environment`. This field was used to enforce immutability on certain
scopes during binding.

The logic for handling the immutability of the root scope (scope 0) has
been moved and refactored. Now, during the `Environment::new` bootstrap
phase, the addresses within the RTL scope (scope 0) are directly
translated from `Address::Local` to `Address::Global`. This ensures that
the Binder inherently treats these as global and immutable without
needing an explicit `fixed_scope_idx` check.

The `Binder::bind_root` signature has been updated to reflect this
change by removing the `fixed_scope_idx` parameter. Consequently, tests
and other usages of `bind_root` have been adjusted.

This change simplifies the binding process by centralizing the
immutability handling to the environment setup phase, making the
Binder's logic cleaner.
2026-03-31 11:19:56 +02:00

5.9 KiB

Analyse von src/ast/environment.rs

1. Struktur von Environment

Die Environment-Struktur fungiert im Projekt als zentraler "State-Manager" und Orchestrator. Sie hält den globalen Zustand für den gesamten Lebenszyklus eines Skripts, von der Quelle bis zur Ausführung:

  • Zustandsverwaltung (State Container): Beinhaltet den globalen Programmzustand in Form von Registern und Caches. Nahezu alles ist in Rc<RefCell<T>> gekapselt (z.B. root_types, root_values, root_scopes, Caches für Optimierung und Monomorphisierung).
  • Modulladung & Abhängigkeiten: preload_dependencies und discover_globals lesen #use-Abhängigkeiten, durchsuchen den Code vorab nach globalen Definitionen (def) und Makros und laden die Standardbibliothek (prelude.myc).
  • Kompilierungs-Pipeline: Die Methoden compile, compile_syntax, compile_pipeline und link steuern den Code durch alle Compiler-Phasen: Macro-Expansion -> Binding -> Type-Checking -> Analysis -> Specialization -> Optimization -> Lowering.
  • Makro-Evaluierung: Die interne Struktur RuntimeMacroEvaluator wird genutzt, um AST-Knoten zur Compile-Zeit an eine VM zu übergeben und den Code für Makros auszuführen.
  • Laufzeit-Ausführung & RTL (Runtime Library): Methoden wie run_script, run_debug und instantiate starten die VM. Zudem gibt es Methoden (register_native, allocate_slot), um native Rust-Funktionen (Intrinsics) im globalen RTL-Scope (Scope 0) zu registrieren.
  • Dokumentations-Registry: Es speichert sowohl RTL-Dokumentation als auch aus dem Source-Code extrahierte Kommentare (myc_docs).

2. Prüfung auf Boilerplate

Der Code weist an mehreren Stellen typischen Rust-Boilerplate für Single-Threaded-Interpreter auf:

  • Das Rc<RefCell>-Muster: Um denselben globalen Zustand zwischen Parser, Compiler, Pipeline und VM zu teilen, bestehen 14 der 19 Felder aus Rc<RefCell<...>>. Das zwingt überall im Code zu redundantem .borrow(), .borrow_mut() und .clone() (z. B. beim Klonen in RuntimeMacroEvaluator über get_expander). Ausnahme: rtl_values: Rc<[Value]> ist bewusst ohne RefCell — die eingefrorenen RTL-Werte sind nach dem Bootstrap-Freeze immutable und werden im VM-Hotpath direkt gelesen.
  • Fehler-Mapping: Beim Modulladen wird oft repetitiv mit .map_err(|e: String| format!("...", e)) Boilerplate geschrieben, anstatt einen zentralen Error-Typen mit thiserror oder anyhow zu verwenden.
  • Manuelle AST-Traversierung: In discover_globals und collect_doc_comments wird der AST per Hand (mittels match und Rekursion) durchlaufen, anstatt ein zentrales "Visitor-Pattern" wiederzuverwenden.

3. Prüfung auf "unscharfe Interfaces" (Fuzzy Interfaces)

Der Code enthält einige starke Indikatoren für unscharfe Systemgrenzen ("Leaky Abstractions") und Vermischung von Zuständigkeiten (God Object Smell):

  • God Object: Environment weiß zu viel. Es parst Dateien, wertet Makros aus, instanziiert den Typ-Prüfer (TypeChecker), betreibt Caching für Optimierer, startet die VM und sammelt nebenbei Doc-Strings. Eine klarere Trennung zwischen CompilerEnvironment (statische Phasen) und RuntimeEnvironment (VM-Zustand/Values) fehlt hier.
  • Verwaschene Kompilierungsschritte: ✓ Behoben (Design korrekt, ein Detail bereinigt). Die drei Methoden bilden eine saubere Adapter-Hierarchie: compile_pipeline (Kern, Diagnostics-Sink) ← compile (public API, merged Parse+Compile-Fehler in CompilationResult) / compile_syntax (privater Adapter für Modul-Loader, konvertiert zu Result<_, String>). Das unterschiedliche Error-Reporting ist situations-appropriat, nicht inkonsistent. Behobener Design-Geruch: dump_ast umging zuvor compile() und verschluckte Parse-Fehler still. Behoben: dump_ast delegiert jetzt an compile(source).into_result()?.
  • Die specialize_node-Closure: ✓ Kein Problem (Design korrekt verstanden). Die compiler-Closure ist kein Zeichen unsauberer Kapselung — im Gegenteil. Specializer ist bewusst von TypeChecker, Optimizer und VM entkoppelt; der CompileFunc-Typ (specializer.rs:15) ist der explizite Dependency-Injection-Punkt (Strategy Pattern). Die innere Pipeline (TypeCheck → Analyze → sub-Specialize → Optimize → Lower → VM.run) dupliziert nicht die äußere — ihr Zweck ist fundamental verschieden: sie erzeugt einen gecachten Value (compile-and-evaluate), während die äußere Pipeline einen ExecNode für die Skriptausführung erzeugt. Die Closure captured 6 Rc<RefCell<T>>-Handles statt &self, weil Rust keine self-Referenz in eine 'static-Closure erlaubt — das ist der korrekte Rust-Weg. Design-Limitation (dokumentiert, kein Bug): Der sub_specializer innerhalb der Closure hat compiler: None, was Endlos-Rekursion verhindert, aber bedeutet, dass nur eine Spezialisierungsebene pro Aufruf expandiert wird.
  • Type-Checker Hack: ✓ Kein Problem (Kommentar bereinigt). Der ursprüngliche Kommentar war veraltet und irreführend. Das BoundLike-Trait vereinigt alle Phasen mit identischer Binding-Struktur (BoundPhase, TypedPhase, AnalyzedPhase). TypeChecker::check_node_as_bound<P: BoundLike>() ist korrekte Generic-Programmierung — kein Hack. Die Phasen passen sauber ineinander. Der Kommentar wurde durch eine korrekte Erklärung des Monomorphisierungs-Designs ersetzt.
  • Konzeptioneller Bruch bei instantiate: ✓ Behoben. instantiate gibt jetzt Result<Rc<Closure>, String> zurück statt Rc<NativeFunction>. Die VM-Erzeugung und -Ausführung liegt beim Aufrufer (run_script_compiled, Benchmark-Runner), der die passende Strategie kennt (einmaliger Run vs. N Iterationen mit VM-Reuse). NativeFunction bleibt ausschließlich für echte Rust-Intrinsics (RTL). Bonus: der Benchmark-Runner erstellt jetzt eine VM für N Iterationen statt N VMs — run_with_args resettet stack und frames am Anfang jedes Aufrufs, VM-Reuse ist sicher.