Add .claudeignore and bytecode architecture documentation

This commit introduces two new files:
- `.claudeignore`: Specifies files and directories that should be
  ignored by Claude, such as snapshot files and the `target` directory.
- `docs/bytecode-architecture.md`: Documents the architecture for
  bytecode generation, outlining the hybrid execution model, the
  transformation pipeline (Specializer and Lowering), and the role of
  Tail Call Optimization (TCO).
This commit is contained in:
2026-03-21 12:43:53 +01:00
parent 72244d9da7
commit 63a474030d
3 changed files with 158 additions and 0 deletions
+4
View File
@@ -0,0 +1,4 @@
*.snap
*.snap.new
target/
docs/delphi/
+73
View File
@@ -0,0 +1,73 @@
# Projekt: AST-Compiler
* Dieses Repository enthält einen unfertigen Compiler für eine DSL, die zur Finanzanalyse konzipiert ist.
### Design
* Der AST soll 1st class citizen sein. Das Skript ist nur eine mögliche Art, ihn darzustellen.
* Eine geplante Darstellung des AST ist vollständige grafische Visualisierung.
* Es wird eine DSL für Finanzanalyse.
* Aufgrund der geforderten Visualisierbarkeit muss der untyped AST (das, was der User bearbeiten kann) möglichst simpel sein. Komplexität muss vom System übernommen werden.
* Closures sind Key-Feature.
### Concurrency
* Die Root-Scopes sind die Grenze für Multithreading. Nichts verlässt den Root-Scope und alles innerhalb des Scope ist Single-Threaded.
* Globals sind nach dem Bootstrapping immutable.
* Da das Skript single-threaded ist, kann auf atomare Operationen (Mutex, Arc) verzichtet werden!
### Spezielle Datentypen
* Serien sind "unendliche" Queues mit maximaler Länge (Lookback). Serie[0] ist das zuletzt gepushte Item.
* Streams sind virtuelle Konfigurationen aus Serien, die in der Lage sind einen neuen Item-Record zu propagieren.
* Pipes sind Streams mit einer weiteren Funktion: Sie können einen Stream als Input akzeptieren und einen anderen Stream als Output produzieren.
## Portierungsregeln
* Wir wollen die Sprachfeatures von Rust nutzen.
* Der Code soll aber so gut wie möglich nach Rust-Style-Guidelines geschrieben werden.
* Dokumentation nach Rust-Regeln.
* Im Code und den Kommentaren muss alles auf englisch sein.
## Rust
* Performance: Bevorzuge `Rc<dyn Trait>` + lokales `downcast_ref` gegenüber Deep Copies via `Rc::new(obj.clone())`.
## Interaktion
* Ich kann Rust noch nicht so gut. Erkläre mir, was du machst und welche Konzepte du nutzt. Ich will was dazulernen. Wenn erforderlich, nutze zur Veranschaulichung Delphi aus Vergleichssprache.
* Wenn clippy etwas vorschlägt, von dem du glaubst, dass es nicht stabil ist, dann überprüfe die aktuelle Version. Wahrscheinlich ist das Feature mittlerweile im trunk. Wir gehen immer davon aus, dass clippy recht hat.
* Keine Interaktion mit GIT. Kein Commit, oder ähnliches. Schlage auch nichts dergleichen vor.
* Gehe immer schrittweise vor. Zeige mir, was du vor hast, bevor du Code erzeugst.
* Sprich im Chat deutsch mit mir.
* Nutze den Repomix-MCP-Server um den Code zu lesen.
## Testing & Debugging
* Das Projekt enthält eine Testsuite für Skripte. Logik in src/utils/tester.rs. Diese Tests werden automatisch in "cargo test" eingebunden.
* Nach Fertigstellung einer Aufgabe, oder wenn ich zum Testen auffordere: Warnungen sind zu eliminieren. Tests müssen durchlaufen. Wenn alles funktioniert, muss auch clippy ohne Beanstandungen durchlaufen.
* Du kannst Skripte selbst testen! Nutze das ast-tool:
> ./target/release/ast.exe --help
MYC AST Compiler & Benchmarker
Usage: ast.exe [OPTIONS] [FILE]
Arguments:
[FILE] The script file to run or benchmark
Options:
-e, --eval <EVAL> Run a script string directly
-b, --bench Run benchmarks (Only allowed in Release mode)
-u, --update-bench Update the benchmark baseline (Only allowed in Release mode)
-d, --dump Dump the compiled AST
-t, --trace Run with TracingObserver enabled
--no-opt Disable optimization
-h, --help Print help
-V, --version Print version
# Sprachspezifikation
* Dieses Dokument sollte aktuell gehalten werden:
@docs/BNF.md
+81
View File
@@ -0,0 +1,81 @@
# Bytecode Generation Architecture
This document describes the transition from a pure tree-walking AST interpreter to a hybrid execution model using specialized bytecode.
## 1. Overview: The Hybrid Model
The MYC VM operates in a hybrid mode. While the Abstract Syntax Tree (AST) is the first-class citizen and primary representation, "hot" paths—specifically monomorphized functions and financial pipeline lambdas—are lowered into a linear bytecode representation.
---
## 2. The Integrated Transformation Pipeline
To ensure maximum performance and correct Tail Call Optimization (TCO), bytecode generation is integrated into the structural optimization phase.
### Step 1: Specializer (Structural Optimization & Emission)
The **Specializer** is the core engine for code improvement.
- **Inlining & Monomorphization:** It first performs all structural changes (e.g., inlining function bodies). This is critical because inlining changes what constitutes a "tail position."
- **Symbolic Emission:** Once the structure of a function/block is final, the Specializer converts it into **Symbolic Bytecode**.
- **Integrated TCO Analysis:** During emission, the Specializer tracks the tail-call state locally. It emits `OP_RECUR` or `OP_TAIL_CALL` based on the final, inlined structure.
- **Variable Mapping:** Instructions still use `VirtualId` (e.g., `OP_GET_VIRTUAL(V102)`).
### Step 2: Lowering (Address Resolution & Linking)
The **Lowering** pass acts as the "Linker". It does not change the structure anymore.
- **Address Resolution:** It walks the symbolic bytecode and replaces `VirtualId` with concrete `StackOffset` values.
- **Stack Allocation:** It calculates the final `stack_size` for the bytecode block.
- **Finalization:** It "freezes" the instruction vector for the `ExecNode`.
---
## 3. Why This Order? (Inlining vs. TCO)
TCO analysis must occur **after** structural optimizations like inlining.
If TCO were analyzed before inlining, a tail-call in a small function might lose its tail position once embedded into a larger caller. By performing TCO analysis **during** bytecode emission (after inlining), we guarantee that the `is_tail` flags are always semantically correct.
---
## 4. Instruction Set Architecture (ISA) & TCO
The ISA uses specialized instructions to bypass the heavy tree-walking logic:
1. **OP_RECUR:**
- Used for the `again` keyword.
- Triggers an immediate jump to PC 0 within the same bytecode executor.
- Repacks the current stack frame with new arguments.
2. **OP_TAIL_CALL(Callee):**
- Used for function calls in tail positions.
- If the callee is bytecode, it performs a fast-swap of the instruction vector.
- If the callee is native/tree, it returns a `TailCallRequest` to the VM dispatcher.
---
## 5. The Hybrid VM Dispatcher
The VM's `eval_internal` remains the master controller. It dispatches to the Bytecode Executor when encountering a `BoundKind::Bytecode` node. The executor can "escape" back to the tree-walker for any operation it cannot handle natively (e.g., calling an opaque extension).
```rust
fn run_bytecode(instructions: &[Instruction], pc: &mut usize) -> Value {
loop {
match instructions[*pc] {
Instruction::PushConst(v) => stack.push(v),
Instruction::AddInt => {
let b = stack.pop();
let a = stack.pop();
stack.push(a + b);
}
Instruction::TailCall(callee) => {
// Return to dispatcher if it's a cross-world call
return self.handle_tail_call(callee);
}
Instruction::CallTree(node) => {
// Re-enter tree-walking for specific node
let res = self.eval_internal(node)?;
stack.push(res);
}
// ...
}
pc += 1;
}
}
```