- 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>
358 lines
9.0 KiB
Rust
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>,
|
|
}
|