Files
RustAst/docs/Analysis_Environment.md
T
Brummel b6dcdbde8d Refactor instantiate to return Closure
The `instantiate` method in `Environment` has been refactored to return
`Result<Rc<Closure>, String>` instead of `Rc<NativeFunction>`. This
change separates the creation of a `Closure` from its execution,
allowing the caller to manage the `VM` lifecycle and choose the
appropriate execution strategy (e.g., single run vs. repeated benchmark
iterations).

The `NativeFunction` type is now exclusively used for true Rust
intrinsics. The benchmark runner has also been updated to reuse a single
`VM` across iterations, improving performance by avoiding repeated VM
allocations.
2026-03-27 22:04:20 +01:00

5.7 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 Scope (fixed_scope_idx = 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 13 der 17 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).
  • 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.