Files
TerminalBasic/crates/tb-frontend/src/ast.rs
Chili Palmer f7e57b0bd8 Phase 2 abgeschlossen: Bytecode, TBVM, Runtime-Scheibe, tbc run
- Sema zum Lowering-Pass umgebaut: typisiertes HIR (Slots, explizite
  Konvertierungsknoten) als Codegen-Eingabe; BYREF verlangt exakten Typ
- Bytecode-Feindesign umgesetzt: monomorpher Opcode-Satz,
  .tbc-Container (Formatversion 1) mit eigenem Writer/Reader
- Codegenerator HIR -> Bytecode (Fixup-Listen, keine globalen Passes)
- TBVM-Interpreter: Kontrollfluss, GOSUB-Stack je Frame, BYREF/BYVAL,
  STATIC, DEF FN, DATA/READ/RESTORE, ON [LOCAL] ERROR/RESUME/ERR/ERL,
  Breakpoints/Einzelschritt/Inspektion, STOP fortsetzbar
- Runtime-Scheibe: Host-Trait (Konsole/Capture), Builtin-Tabelle,
  Konvertierungsmatrix, PRINT-Formatierung/Druckzonen, Stringfunktionen
- tbc run/build/check mit Exit-Codes nach Entscheidung D6
- Korpus-Harness (byte-genauer Vergleich) + 3 neue Korpusdateien
  (konvertierung, fehlerbehandlung, byref); 137 Tests gruen
- Benchmarks: Einzelmodul 1,2 ms / Projekt 49.760 Zeilen 124 ms
  (Budgets eingehalten), VM ~5 Mio Schleifeniterationen/s
- Doku fortgeschrieben (tbvm-design, sprachreferenz, PLAN);
  verlagerte Punkte als explizite Aufgaben in Phase 3
- OpenSpec-Change phase-2-bytecode-vm (27/27 Tasks)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-02 11:28:07 +02:00

358 lines
9.0 KiB
Rust

//! AST-Definitionen: Module, Prozeduren, Anweisungen, Ausdrücke,
//! Deklarationen und benutzerdefinierte Typen.
use crate::lexer::Suffix;
use crate::SourcePos;
/// Typangabe in `AS`-Klauseln.
#[derive(Debug, Clone, PartialEq)]
pub enum TypeName {
Integer,
Long,
Single,
Double,
Currency,
Str,
FixedStr(i64),
Udt(String),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum UnOp {
Neg,
Not,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BinOp {
Pow, Mul, Div, IntDiv, Mod, Add, Sub,
Eq, Ne, Lt, Le, Gt, Ge,
And, Or, Xor, Eqv, Imp,
}
#[derive(Debug, Clone, PartialEq)]
pub enum Expr {
IntLit(i16),
LongLit(i32),
SingleLit(f32),
DoubleLit(f64),
CurrencyLit(i64),
StrLit(String),
/// Benannter Zugriff: Variable, Arrayelement, Funktionsaufruf oder
/// Konstante — Auflösung erfolgt in der Semantik.
Name {
name: String,
suffix: Option<Suffix>,
args: Option<Vec<Expr>>,
pos: SourcePos,
},
Unary { op: UnOp, operand: Box<Expr>, pos: SourcePos },
Binary { op: BinOp, lhs: Box<Expr>, rhs: Box<Expr>, pos: SourcePos },
/// Geklammerter Ausdruck. Semantisch transparent, aber als Argument
/// erzwingt die Klammer Wertübergabe (BYVAL) statt BYREF.
Paren(Box<Expr>),
/// Ausgelassenes Argument (`LOCATE , 5`).
Missing,
}
impl Expr {
pub fn pos(&self) -> SourcePos {
match self {
Expr::Name { pos, .. }
| Expr::Unary { pos, .. }
| Expr::Binary { pos, .. } => *pos,
Expr::Paren(e) => e.pos(),
_ => SourcePos::default(),
}
}
}
/// Sprungziel: alphanumerisches Label oder Zeilennummer.
#[derive(Debug, Clone, PartialEq)]
pub enum LabelRef {
Name(String),
Line(u32),
}
#[derive(Debug, Clone, PartialEq)]
pub enum PrintItem {
Expr(Expr),
Comma,
Semicolon,
}
#[derive(Debug, Clone, PartialEq)]
pub enum CaseSpec {
Expr(Expr),
Range(Expr, Expr),
Is(BinOp, Expr),
}
#[derive(Debug, Clone, PartialEq)]
pub struct CaseArm {
/// Leer = `CASE ELSE`.
pub specs: Vec<CaseSpec>,
pub body: Vec<Stmt>,
pub pos: SourcePos,
}
#[derive(Debug, Clone, PartialEq)]
pub struct VarDecl {
pub name: String,
pub suffix: Option<Suffix>,
/// `None` = Skalar; leerer Vec = Array ohne Dimensionsangabe (`a()`).
pub dims: Option<Vec<(Option<Expr>, Expr)>>,
pub as_type: Option<TypeName>,
pub pos: SourcePos,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ExitKind {
For,
Do,
Sub,
Function,
Def,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OptionKind {
Explicit,
Base(u8),
}
#[derive(Debug, Clone, PartialEq)]
pub enum OnErrorAction {
Goto(LabelRef),
ResumeNext,
Disable, // GOTO 0
}
#[derive(Debug, Clone, PartialEq)]
pub enum ResumeKind {
Retry, // RESUME [0]
Next,
Label(LabelRef),
}
#[derive(Debug, Clone, PartialEq)]
pub struct Param {
pub name: String,
pub suffix: Option<Suffix>,
pub array: bool,
pub as_type: Option<TypeName>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ProcKind {
Sub,
Function,
}
#[derive(Debug, Clone, PartialEq)]
pub struct ProcSig {
pub kind: ProcKind,
pub name: String,
pub suffix: Option<Suffix>,
pub params: Vec<Param>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Proc {
pub sig: ProcSig,
pub is_static: bool,
pub body: Vec<Stmt>,
pub pos: SourcePos,
}
/// `OPEN … FOR modus`.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OpenMode {
Input,
Output,
Append,
Random,
Binary,
Isam,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AccessMode {
Read,
Write,
ReadWrite,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LockClause {
Shared,
LockRead,
LockWrite,
LockReadWrite,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EventAction {
On,
Off,
Stop,
}
#[derive(Debug, Clone, PartialEq)]
pub enum Stmt {
Label(String),
LineNumber(u32),
Assign { target: Expr, value: Expr, pos: SourcePos },
Print {
/// LPRINT (Druckerausgabe) statt PRINT.
printer: bool,
file: Option<Expr>,
using: Option<Expr>,
items: Vec<PrintItem>,
pos: SourcePos,
},
Input {
line: bool,
file: Option<Expr>,
keep_cursor: bool,
prompt: Option<(String, bool)>, // (Text, mit Fragezeichen)
vars: Vec<Expr>,
pos: SourcePos,
},
If {
cond: Expr,
then_body: Vec<Stmt>,
elseifs: Vec<(Expr, Vec<Stmt>)>,
else_body: Option<Vec<Stmt>>,
pos: SourcePos,
},
Select { expr: Expr, arms: Vec<CaseArm>, pos: SourcePos },
For {
var: Expr,
from: Expr,
to: Expr,
step: Option<Expr>,
body: Vec<Stmt>,
pos: SourcePos,
},
DoLoop {
pre: Option<(bool, Expr)>, // (ist UNTIL, Bedingung)
post: Option<(bool, Expr)>,
body: Vec<Stmt>,
pos: SourcePos,
},
While { cond: Expr, body: Vec<Stmt>, pos: SourcePos },
Goto { target: LabelRef, pos: SourcePos },
Gosub { target: LabelRef, pos: SourcePos },
OnGoto { expr: Expr, targets: Vec<LabelRef>, gosub: bool, pos: SourcePos },
Return { target: Option<LabelRef>, pos: SourcePos },
End(SourcePos),
StopStmt(SourcePos),
System(SourcePos),
Exit { kind: ExitKind, pos: SourcePos },
Dim { shared: bool, redim: bool, decls: Vec<VarDecl>, pos: SourcePos },
/// `SHARED`-Anweisung in einer Prozedur (Zugriff auf Modulvariablen).
SharedDecl { decls: Vec<VarDecl>, pos: SourcePos },
/// `STATIC`-Anweisung in einer Prozedur.
StaticDecl { decls: Vec<VarDecl>, pos: SourcePos },
/// `COMMON [SHARED] [/block/] liste`.
CommonDecl {
shared: bool,
block: Option<String>,
decls: Vec<VarDecl>,
pos: SourcePos,
},
Erase { names: Vec<Expr>, pos: SourcePos },
ConstDecl { items: Vec<(String, Option<Suffix>, Expr)>, pos: SourcePos },
DefType { ty: TypeName, ranges: Vec<(char, char)>, pos: SourcePos },
OptionStmt { kind: OptionKind, pos: SourcePos },
TypeDecl { name: String, fields: Vec<(String, TypeName)>, pos: SourcePos },
Declare { sig: ProcSig, pos: SourcePos },
/// Expliziter oder impliziter Prozedur-/Builtin-Aufruf als Anweisung.
Call { name: String, suffix: Option<Suffix>, args: Vec<Expr>, pos: SourcePos },
OnError { local: bool, action: OnErrorAction, pos: SourcePos },
Resume { kind: ResumeKind, pos: SourcePos },
ErrorStmt { code: Expr, pos: SourcePos },
Data { items: Vec<String>, pos: SourcePos },
ReadStmt { vars: Vec<Expr>, pos: SourcePos },
Restore { target: Option<LabelRef>, pos: SourcePos },
/// Einzeilige `DEF FNname(...) = ausdruck`-Definition.
DefFn {
name: String,
suffix: Option<Suffix>,
params: Vec<Param>,
body: Expr,
pos: SourcePos,
},
/// Blockform `DEF FNname(...) … END DEF`.
DefFnBlock {
name: String,
suffix: Option<Suffix>,
params: Vec<Param>,
body: Vec<Stmt>,
pos: SourcePos,
},
// ---- Datei-E/A (Laufzeit in Phase 3, Grammatik vollständig) ----
Open {
file: Expr,
mode: Option<OpenMode>,
/// Bei `FOR ISAM typname tabellenname`.
isam: Option<(String, String)>,
access: Option<AccessMode>,
lock: Option<LockClause>,
number: Expr,
len: Option<Expr>,
pos: SourcePos,
},
/// Alte Syntax `OPEN modus$, [#]n, datei$ [, reclen]`.
OpenLegacy {
mode: Expr,
number: Expr,
file: Expr,
len: Option<Expr>,
pos: SourcePos,
},
CloseStmt { files: Vec<Expr>, pos: SourcePos },
FieldStmt { file: Expr, fields: Vec<(Expr, Expr)>, pos: SourcePos },
GetPut {
put: bool,
file: Expr,
recnum: Option<Expr>,
var: Option<Expr>,
pos: SourcePos,
},
LsetRset { rset: bool, target: Expr, value: Expr, pos: SourcePos },
WriteStmt { file: Option<Expr>, items: Vec<Expr>, pos: SourcePos },
SeekStmt { file: Expr, position: Expr, pos: SourcePos },
LockStmt {
unlock: bool,
file: Expr,
from: Option<Expr>,
to: Option<Expr>,
pos: SourcePos,
},
NameStmt { old: Expr, new: Expr, pos: SourcePos },
// ---- Bildschirm/Ereignisse ----
/// `VIEW PRINT [oben TO unten]`.
ViewPrint { top: Option<Expr>, bottom: Option<Expr>, pos: SourcePos },
/// `TIMER ON`, `KEY(5) OFF`, `UEVENT STOP` …
EventControl {
device: String,
index: Option<Expr>,
action: EventAction,
pos: SourcePos,
},
// ---- Metabefehle ----
/// `'$INCLUDE: 'datei''` — Auflösung übernimmt der Compile-Treiber.
Include { path: String, pos: SourcePos },
/// `'$STATIC` / `'$DYNAMIC`.
MetaArrays { static_arrays: bool, pos: SourcePos },
}
#[derive(Debug, Clone, PartialEq)]
pub struct Module {
pub name: String,
pub body: Vec<Stmt>,
pub procs: Vec<Proc>,
}