Files
RustAst/docs/Analysis_Environment.md
T
Brummel 3d0ea094b0 Fix: Dump AST ignores parse errors
The `dump_ast` method previously bypassed the `compile` method, which
meant it would silently ignore parsing errors. This commit ensures that
`dump_ast` now delegates to `compile` and correctly propagates any
parsing errors. This aligns the behavior of `dump_ast` with other
compilation steps and provides better feedback to the user.
Fix: Dump AST ignores parse errors

The `dump_ast` method now delegates to `compile(source).into_result()?`
to properly handle and report parse errors, ensuring consistency with
the compilation process.
2026-03-27 21:36:01 +01:00

5.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: ✓ 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: 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.