Files
RustAst/docs/Analysis_Environment.md
T

4.5 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: Die Methoden compile, compile_syntax und compile_pipeline haben überlappende Aufgabenbereiche. Mal wird das Error-Reporting über eine Diagnostics-Instanz gemacht, mal wird diese verworfen und stumpf in einen Result<TypedNode, String> umgewandelt.
  • Die specialize_node-Closure: In specialize_node wird eine gigantische Inline-Closure (compiler = Rc::new(move |...| { ... })) definiert. Diese baut tief im Inneren eine komplette Mini-Compiler-Pipeline samt VM-Ausführung nach, nur um Konstanten zu falten / Aufrufe zu spezialisieren. Das zeigt, dass die Compiler-Pipeline nach außen hin nicht sauber gekapselt ist.
  • 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: Diese Methode nimmt einen fertig kompilierten Ast (ExecNode) und wickelt ihn in eine NativeFunction ein. Dadurch wird ein in Myc geschriebenes Skript in der Registry strukturell als "native Rust-Funktion" getarnt. Das ist pragmatisch, verwischt aber die semantische Grenze zwischen User-Code (Closures) und echten Runtime-Intrinsics.