35f5ea0db3
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.
5.9 KiB
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_dependenciesunddiscover_globalslesen#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_pipelineundlinksteuern den Code durch alle Compiler-Phasen: Macro-Expansion -> Binding -> Type-Checking -> Analysis -> Specialization -> Optimization -> Lowering. - Makro-Evaluierung: Die interne Struktur
RuntimeMacroEvaluatorwird 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_debugundinstantiatestarten dieVM. 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 ausRc<RefCell<...>>. Das zwingt überall im Code zu redundantem.borrow(),.borrow_mut()und.clone()(z. B. beim Klonen inRuntimeMacroEvaluatorüberget_expander). Ausnahme:rtl_values: Rc<[Value]>ist bewusst ohneRefCell— 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 zentralenError-Typen mitthiserroroderanyhowzu verwenden. - Manuelle AST-Traversierung: In
discover_globalsundcollect_doc_commentswird der AST per Hand (mittelsmatchund 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:
Environmentweiß zu viel. Es parst Dateien, wertet Makros aus, instanziiert den Typ-Prüfer (TypeChecker), betreibt Caching für Optimierer, startet dieVMund sammelt nebenbei Doc-Strings. Eine klarere Trennung zwischenCompilerEnvironment(statische Phasen) undRuntimeEnvironment(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 inCompilationResult) /compile_syntax(privater Adapter für Modul-Loader, konvertiert zuResult<_, String>). Das unterschiedliche Error-Reporting ist situations-appropriat, nicht inkonsistent. Behobener Design-Geruch:dump_astumging zuvorcompile()und verschluckte Parse-Fehler still. Behoben:dump_astdelegiert jetzt ancompile(source).into_result()?.Die✓ Kein Problem (Design korrekt verstanden). Diespecialize_node-Closure:compiler-Closure ist kein Zeichen unsauberer Kapselung — im Gegenteil.Specializerist bewusst von TypeChecker, Optimizer und VM entkoppelt; derCompileFunc-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 gecachtenValue(compile-and-evaluate), während die äußere Pipeline einenExecNodefür die Skriptausführung erzeugt. Die Closure captured 6Rc<RefCell<T>>-Handles statt&self, weil Rust keineself-Referenz in eine'static-Closure erlaubt — das ist der korrekte Rust-Weg. Design-Limitation (dokumentiert, kein Bug): Dersub_specializerinnerhalb der Closure hatcompiler: 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. DasBoundLike-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✓ Behoben.instantiate:instantiategibt jetztResult<Rc<Closure>, String>zurück stattRc<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).NativeFunctionbleibt 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_argsresettetstackundframesam Anfang jedes Aufrufs, VM-Reuse ist sicher.