717 lines
16 KiB
Rust
717 lines
16 KiB
Rust
//! Typisiertes, abgesenktes HIR — die Ausgabe der semantischen Analyse
|
||
//! und Eingabe des Codegenerators (`tb-vm`).
|
||
//!
|
||
//! Eigenschaften (siehe Design der Phase-2-Änderung):
|
||
//! - Namen sind aufgelöst: Variablen sind Slot-Indizes (global/lokal),
|
||
//! Prozeduren und UDTs Tabellenindizes, Sprungziele `LabelId`s je Rumpf.
|
||
//! - Jeder Ausdrucksknoten trägt seinen Ergebnistyp; implizite
|
||
//! Konvertierungen sind als explizite `Conv`-Knoten materialisiert
|
||
//! (Semantik: Konvertierungsmatrix in docs/tbvm-design.md).
|
||
//! - Kontrollzucker ist abgesenkt: `SELECT CASE` zu Vergleichsketten,
|
||
//! `ELSEIF` zu verschachteltem `If`, `EXIT FOR/DO` zu `Goto` auf
|
||
//! synthetisierte Labels, `SWAP` zu Zuweisungen über einen Temp-Slot.
|
||
//! - `STATIC`-Locals und versteckte Temps liegen im globalen Slot-Bereich.
|
||
//!
|
||
//! Ein vollständiges, korrektes HIR ist nur bei diagnose-freier Analyse
|
||
//! garantiert.
|
||
|
||
/// Numerischer Skalartyp (Kürzel wie im Opcode-Satz: I2/I4/CY/R4/R8).
|
||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||
pub enum NumTy {
|
||
Int,
|
||
Lng,
|
||
Cur,
|
||
Sng,
|
||
Dbl,
|
||
}
|
||
|
||
/// Ganzzahlbreite der Logik-Operatoren.
|
||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||
pub enum IntKind {
|
||
I2,
|
||
I4,
|
||
}
|
||
|
||
/// Aufgelöster HIR-Typ.
|
||
#[derive(Debug, Clone, PartialEq)]
|
||
pub enum HTy {
|
||
Num(NumTy),
|
||
Str,
|
||
/// Fester String mit Zeichenlänge (Zuweisung padded/kürzt).
|
||
FixedStr(u32),
|
||
/// Benutzerdefinierter Typ (Index in `HirModule::udts`).
|
||
Udt(u16),
|
||
Form,
|
||
Control,
|
||
}
|
||
|
||
impl HTy {
|
||
pub fn num(&self) -> Option<NumTy> {
|
||
match self {
|
||
HTy::Num(n) => Some(*n),
|
||
_ => None,
|
||
}
|
||
}
|
||
pub fn is_str(&self) -> bool {
|
||
matches!(self, HTy::Str | HTy::FixedStr(_))
|
||
}
|
||
}
|
||
|
||
/// Slot-Referenz: globaler Bereich (Modulvariablen, STATICs, versteckte
|
||
/// Temps des Hauptprogramms) oder Frame-lokal (Parameter zuerst).
|
||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||
pub enum VarSlot {
|
||
Global(u16),
|
||
Local(u16),
|
||
}
|
||
|
||
/// Sprungziel innerhalb eines Prozedurrumpfs.
|
||
pub type LabelId = u16;
|
||
|
||
/// Variablen-/Slotbeschreibung (auch für die Debugger-Inspektion).
|
||
#[derive(Debug, Clone)]
|
||
pub struct HVar {
|
||
pub name: String,
|
||
pub ty: HTy,
|
||
pub array: bool,
|
||
}
|
||
|
||
#[derive(Debug, Clone)]
|
||
pub struct HUdt {
|
||
pub name: String,
|
||
pub fields: Vec<(String, HTy)>,
|
||
}
|
||
|
||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||
pub enum HProcKind {
|
||
/// Hauptprogramm (Modulrumpf) — immer Prozedur 0.
|
||
Main,
|
||
Sub,
|
||
Function,
|
||
/// `DEF FN` — Parameter BYVAL, freie Namen binden an Modulvariablen.
|
||
DefFn,
|
||
}
|
||
|
||
#[derive(Debug, Clone)]
|
||
pub struct HParam {
|
||
pub name: String,
|
||
pub ty: HTy,
|
||
pub array: bool,
|
||
/// Skalar-Parameter, der als Referenz übergeben wird (Arrays und
|
||
/// UDTs sind implizit immer Referenzen auf ihr Handle).
|
||
pub by_ref: bool,
|
||
}
|
||
|
||
#[derive(Debug)]
|
||
pub struct HProc {
|
||
pub name: String,
|
||
pub kind: HProcKind,
|
||
pub params: Vec<HParam>,
|
||
/// Alle Frame-Slots; `params.len()` erste Einträge sind die Parameter.
|
||
pub locals: Vec<HVar>,
|
||
/// Slot der Rückgabevariablen (Function/DefFn).
|
||
pub ret_slot: Option<VarSlot>,
|
||
pub ret_ty: Option<HTy>,
|
||
pub body: Vec<HStmt>,
|
||
/// Anzahl vergebener LabelIds in diesem Rumpf.
|
||
pub label_count: u16,
|
||
}
|
||
|
||
/// Eine DATA-Konstante (unkonvertiert; `READ` konvertiert zur Laufzeit).
|
||
#[derive(Debug, Clone)]
|
||
pub struct DataItem {
|
||
pub text: String,
|
||
pub line: u32,
|
||
}
|
||
|
||
/// Aufgelöste COMMON-Deklaration für die projektweite Slotverknüpfung.
|
||
#[derive(Debug, Clone)]
|
||
pub struct HCommon {
|
||
pub slot: u16,
|
||
pub block: Option<String>,
|
||
pub key: String,
|
||
pub ty: HTy,
|
||
pub dims: Option<Vec<(Option<i32>, Option<i32>)>>,
|
||
pub pos: crate::SourcePos,
|
||
}
|
||
|
||
#[derive(Debug)]
|
||
pub struct HirModule {
|
||
pub name: String,
|
||
pub globals: Vec<HVar>,
|
||
pub commons: Vec<HCommon>,
|
||
pub udts: Vec<HUdt>,
|
||
/// Prozeduren; Index 0 ist das Hauptprogramm.
|
||
pub procs: Vec<HProc>,
|
||
pub data: Vec<DataItem>,
|
||
pub option_base: u8,
|
||
/// Zur Übersetzungszeit bekannte Forms-Objekte in Indexreihenfolge.
|
||
pub objects: Vec<crate::forms::FormObject>,
|
||
/// Ereignisprozeduren: Objektindex, Ereignisname, Prozedurindex.
|
||
pub event_procs: Vec<HEventProc>,
|
||
}
|
||
|
||
#[derive(Debug, Clone)]
|
||
pub struct HEventProc {
|
||
pub object: u16,
|
||
pub event: String,
|
||
pub proc: u16,
|
||
}
|
||
|
||
// ---- Ausdrücke -------------------------------------------------------------
|
||
|
||
/// L-Wert: Basis-Slot, optional Array-Indizes, optional UDT-Feldpfad.
|
||
#[derive(Debug, Clone)]
|
||
pub struct HPlace {
|
||
pub base: VarSlot,
|
||
/// Skalar-BYREF-Parameter: der Slot enthält eine Referenz.
|
||
pub base_is_ref: bool,
|
||
/// Array-Elementzugriff (leer = Skalar bzw. ganzes Array).
|
||
pub indices: Vec<HExpr>,
|
||
/// UDT-Feldpfad (Feldindizes je Ebene).
|
||
pub fields: Vec<u16>,
|
||
/// Typ des adressierten Werts.
|
||
pub ty: HTy,
|
||
/// Element-/Basistyp und Dimension für Auto-DIM impliziter Arrays.
|
||
pub array_elem: Option<(HTy, u8)>,
|
||
}
|
||
|
||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||
pub enum HArith {
|
||
Add,
|
||
Sub,
|
||
Mul,
|
||
Div,
|
||
IDiv,
|
||
Mod,
|
||
Pow,
|
||
}
|
||
|
||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||
pub enum HCmp {
|
||
Eq,
|
||
Ne,
|
||
Lt,
|
||
Le,
|
||
Gt,
|
||
Ge,
|
||
}
|
||
|
||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||
pub enum HLogic {
|
||
And,
|
||
Or,
|
||
Xor,
|
||
Eqv,
|
||
Imp,
|
||
}
|
||
|
||
/// Vergleichs-Operandentyp.
|
||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||
pub enum CmpKind {
|
||
Num(NumTy),
|
||
Str,
|
||
}
|
||
|
||
/// Bibliotheksfunktionen/-anweisungen der Phase-2-Scheibe. Der
|
||
/// Diskriminant ist zugleich der stabile Index der Dispatch-Tabelle
|
||
/// (`CALL_BUILTIN`); Phase 3 erweitert ausschließlich am Ende.
|
||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||
#[repr(u16)]
|
||
pub enum Builtin {
|
||
// Strings
|
||
Len,
|
||
LeftS,
|
||
RightS,
|
||
MidS,
|
||
InstrF,
|
||
UcaseS,
|
||
LcaseS,
|
||
LtrimS,
|
||
RtrimS,
|
||
SpaceS,
|
||
StringS,
|
||
ChrS,
|
||
Asc,
|
||
StrS,
|
||
Val,
|
||
HexS,
|
||
OctS,
|
||
/// MID$-Anweisung als reine Funktion: (ziel, start, länge, ersatz) → neuer String.
|
||
MidAssign,
|
||
// Mathematik
|
||
Abs,
|
||
Sgn,
|
||
IntF,
|
||
Fix,
|
||
Sqr,
|
||
Exp,
|
||
Log,
|
||
Sin,
|
||
Cos,
|
||
Tan,
|
||
Atn,
|
||
Rnd,
|
||
Randomize,
|
||
// Konsole (PRINT-Familie; Wirkung über Host + Druckspalten-Zustand)
|
||
PrintVal,
|
||
PrintStrLit,
|
||
PrintComma,
|
||
PrintTab,
|
||
PrintSpc,
|
||
PrintNewline,
|
||
PrintUsing,
|
||
FormatS,
|
||
SetFormatCc,
|
||
// Bildschirm (Anweisungen)
|
||
Cls,
|
||
Color,
|
||
Locate,
|
||
Width,
|
||
ViewPrint,
|
||
ScreenStmt,
|
||
KeyAssign,
|
||
KeyList,
|
||
KeyDisplay,
|
||
// Bildschirm (Funktionen)
|
||
Csrlin,
|
||
PosFn,
|
||
ScreenFn,
|
||
// Tastatur
|
||
InkeyS,
|
||
InputS,
|
||
// System
|
||
EnvironS,
|
||
EnvironSet,
|
||
Fre,
|
||
Clear,
|
||
Tron,
|
||
Troff,
|
||
StackFn,
|
||
StackStmt,
|
||
Erdev,
|
||
ErdevS,
|
||
// Datei-E/A
|
||
Open,
|
||
Close,
|
||
CloseAll,
|
||
PrintZiel,
|
||
WriteFile,
|
||
EofF,
|
||
LofF,
|
||
LocF,
|
||
SeekF,
|
||
SeekStmt,
|
||
Freefile,
|
||
Fileattr,
|
||
LockStmt,
|
||
Kill,
|
||
NameStmt,
|
||
Files,
|
||
Chdir,
|
||
Chdrive,
|
||
Mkdir,
|
||
Rmdir,
|
||
CurdirS,
|
||
DirS,
|
||
Lpos,
|
||
ShellStmt,
|
||
ShellFn,
|
||
MkS,
|
||
CvF,
|
||
// Finanzmathematik
|
||
Fv,
|
||
Pv,
|
||
Pmt,
|
||
NPer,
|
||
IPmt,
|
||
PPmt,
|
||
Rate,
|
||
Npv,
|
||
Irr,
|
||
Mirr,
|
||
Sln,
|
||
Syd,
|
||
Ddb,
|
||
// Sonstiges
|
||
Timer,
|
||
DateS,
|
||
TimeS,
|
||
DateSet,
|
||
TimeSet,
|
||
Now,
|
||
TimezoneKnown,
|
||
DateSerial,
|
||
TimeSerial,
|
||
DateValue,
|
||
TimeValue,
|
||
DayF,
|
||
MonthF,
|
||
YearF,
|
||
WeekdayF,
|
||
HourF,
|
||
MinuteF,
|
||
SecondF,
|
||
CommandS,
|
||
Doevents,
|
||
Sleep,
|
||
/// `SetUEvent` — löst das benutzerdefinierte Ereignis aus (§8).
|
||
SetUEvent,
|
||
Beep,
|
||
// ISAM (Change `phase-3-isam`)
|
||
IsamOpen,
|
||
IsamCreateIndex,
|
||
IsamDeleteIndex,
|
||
IsamSetIndex,
|
||
IsamGetIndexS,
|
||
IsamInsert,
|
||
IsamRetrieve,
|
||
IsamUpdate,
|
||
IsamDelete,
|
||
IsamDeleteTable,
|
||
IsamMoveFirst,
|
||
IsamMoveLast,
|
||
IsamMoveNext,
|
||
IsamMovePrevious,
|
||
IsamSeekEq,
|
||
IsamSeekGt,
|
||
IsamSeekGe,
|
||
IsamBeginTrans,
|
||
IsamCommitTrans,
|
||
IsamRollback,
|
||
IsamSavepoint,
|
||
IsamSetmem,
|
||
IsamBof,
|
||
MsgBox,
|
||
InputBoxS,
|
||
ClipboardAdd,
|
||
ClipboardGet,
|
||
GraphicsLine,
|
||
GraphicsPaint,
|
||
GraphicsView,
|
||
}
|
||
|
||
#[derive(Debug, Clone)]
|
||
pub enum HExpr {
|
||
Int(i16),
|
||
Lng(i32),
|
||
/// TYPE-Tabellenreferenz für ISAM; beim Projektlinken zu versetzen.
|
||
UdtId(u16),
|
||
Sng(f32),
|
||
Dbl(f64),
|
||
Cur(i64),
|
||
Str(String),
|
||
Load(Box<HPlace>),
|
||
ObjectProperty {
|
||
object: u16,
|
||
index: Option<Box<HExpr>>,
|
||
property: u16,
|
||
ty: HTy,
|
||
},
|
||
ObjectIndexedProperty {
|
||
object: u16,
|
||
object_index: Option<Box<HExpr>>,
|
||
property: u16,
|
||
index: Box<HExpr>,
|
||
ty: HTy,
|
||
},
|
||
ObjectMethodCall {
|
||
object: u16,
|
||
index: Option<Box<HExpr>>,
|
||
method: u16,
|
||
args: Vec<HExpr>,
|
||
ty: HTy,
|
||
},
|
||
DynamicObjectProperty {
|
||
object: Box<HExpr>,
|
||
property: String,
|
||
},
|
||
ObjectRef {
|
||
object: u16,
|
||
index: Option<Box<HExpr>>,
|
||
class: crate::forms::ObjectClass,
|
||
},
|
||
TypeOf {
|
||
value: Box<HExpr>,
|
||
class: crate::forms::ObjectClass,
|
||
},
|
||
/// Numerische Konvertierung nach Matrix (Rundung/Überlauf).
|
||
Conv {
|
||
from: NumTy,
|
||
to: NumTy,
|
||
arg: Box<HExpr>,
|
||
},
|
||
/// Kürzen/Padden auf feste Stringlänge.
|
||
FixStr {
|
||
len: u32,
|
||
arg: Box<HExpr>,
|
||
},
|
||
Neg {
|
||
ty: NumTy,
|
||
arg: Box<HExpr>,
|
||
},
|
||
/// Monomorphe Arithmetik: beide Operanden und das Ergebnis haben `ty`
|
||
/// (bei `Div`/`Pow` nur R4/R8, bei `IDiv`/`Mod` nur I2/I4).
|
||
Bin {
|
||
op: HArith,
|
||
ty: NumTy,
|
||
l: Box<HExpr>,
|
||
r: Box<HExpr>,
|
||
},
|
||
Not {
|
||
ty: IntKind,
|
||
arg: Box<HExpr>,
|
||
},
|
||
Logic {
|
||
op: HLogic,
|
||
ty: IntKind,
|
||
l: Box<HExpr>,
|
||
r: Box<HExpr>,
|
||
},
|
||
/// Vergleich; Ergebnis ist INTEGER (−1/0).
|
||
Cmp {
|
||
op: HCmp,
|
||
ty: CmpKind,
|
||
l: Box<HExpr>,
|
||
r: Box<HExpr>,
|
||
},
|
||
Concat(Box<HExpr>, Box<HExpr>),
|
||
/// FUNCTION-/DEF FN-Aufruf.
|
||
FnCall {
|
||
proc: u16,
|
||
args: Vec<HArg>,
|
||
ret: HTy,
|
||
},
|
||
Builtin {
|
||
b: Builtin,
|
||
args: Vec<HExpr>,
|
||
ret: HTy,
|
||
},
|
||
/// LBOUND/UBOUND eines Arrays.
|
||
ArrayBound {
|
||
lower: bool,
|
||
place: Box<HPlace>,
|
||
dim: Box<HExpr>,
|
||
},
|
||
Err,
|
||
Erl,
|
||
/// Dokumentiertes, aber erst in einer späteren Phase implementiertes
|
||
/// Feature: löst zur Laufzeit Fehler 73 „Advanced feature" aus.
|
||
Unsupported(&'static str),
|
||
}
|
||
|
||
/// Prozedurargument.
|
||
#[derive(Debug, Clone)]
|
||
pub enum HArg {
|
||
ByRef(HPlace),
|
||
/// Ganzes Array als Referenz (`prozedur a()`).
|
||
ArrayRef(HPlace),
|
||
ByVal(HExpr),
|
||
}
|
||
|
||
// ---- Anweisungen -----------------------------------------------------------
|
||
|
||
#[derive(Debug, Clone)]
|
||
pub enum HPrintItem {
|
||
Val(HExpr),
|
||
Tab(HExpr),
|
||
Spc(HExpr),
|
||
Comma,
|
||
}
|
||
|
||
#[derive(Debug, Clone)]
|
||
pub enum HResume {
|
||
Retry,
|
||
Next,
|
||
Label(LabelId),
|
||
}
|
||
|
||
#[derive(Debug)]
|
||
pub struct HStmt {
|
||
pub pos: crate::SourcePos,
|
||
pub line: u32,
|
||
pub kind: HStmtKind,
|
||
}
|
||
|
||
#[derive(Debug)]
|
||
pub enum HStmtKind {
|
||
/// Numerische Zeilennummer durchlaufen (setzt `ERL`).
|
||
SetErl(u32),
|
||
Label(LabelId),
|
||
Assign {
|
||
place: HPlace,
|
||
value: HExpr,
|
||
},
|
||
SetObjectProperty {
|
||
object: u16,
|
||
index: Option<HExpr>,
|
||
property: u16,
|
||
value: HExpr,
|
||
},
|
||
SetObjectIndexedProperty {
|
||
object: u16,
|
||
object_index: Option<HExpr>,
|
||
property: u16,
|
||
index: HExpr,
|
||
value: HExpr,
|
||
},
|
||
SetDynamicObjectProperty {
|
||
object: HExpr,
|
||
property: String,
|
||
value: HExpr,
|
||
},
|
||
ObjectMethod {
|
||
object: u16,
|
||
index: Option<HExpr>,
|
||
method: u16,
|
||
args: Vec<HExpr>,
|
||
},
|
||
ObjectLoad {
|
||
object: u16,
|
||
index: Option<HExpr>,
|
||
unload: bool,
|
||
},
|
||
Print {
|
||
items: Vec<HPrintItem>,
|
||
/// Endet die Anweisung mit `;`/`,` (kein Zeilenumbruch)?
|
||
trailing: bool,
|
||
},
|
||
/// `FIELD #n, laenge AS var$, …` — Recordpuffer in Felder aufteilen.
|
||
Field {
|
||
file: HExpr,
|
||
fields: Vec<(HExpr, HPlace)>,
|
||
},
|
||
/// `LSET`/`RSET` — links- bzw. rechtsbündig zuweisen; ist das Ziel ein
|
||
/// `FIELD`-Feld, wirkt die Zuweisung zugleich auf den Recordpuffer.
|
||
LsetRset {
|
||
rset: bool,
|
||
target: HPlace,
|
||
value: HExpr,
|
||
},
|
||
/// `GET`/`PUT` auf einer Datei: Datensatz- bzw. Bytenummer und Ziel-
|
||
/// bzw. Quellvariable. Ohne Variable wirkt der Recordpuffer.
|
||
GetPut {
|
||
put: bool,
|
||
file: HExpr,
|
||
recnum: Option<HExpr>,
|
||
var: Option<HPlace>,
|
||
},
|
||
Input {
|
||
/// `INPUT #n` — Quelle ist eine Datei statt der Tastatur.
|
||
file: Option<HExpr>,
|
||
line_mode: bool,
|
||
prompt: Option<String>,
|
||
/// Fragezeichen nach dem Prompt (`;`-Form).
|
||
question: bool,
|
||
targets: Vec<HPlace>,
|
||
},
|
||
If {
|
||
cond: HExpr,
|
||
then: Vec<HStmt>,
|
||
els: Vec<HStmt>,
|
||
},
|
||
/// DO/LOOP, WHILE/WEND (nur `pre`) — Bedingungen: (ist_until, Ausdruck).
|
||
Loop {
|
||
end_pos: crate::SourcePos,
|
||
pre: Option<(bool, HExpr)>,
|
||
post: Option<(bool, HExpr)>,
|
||
body: Vec<HStmt>,
|
||
exit_label: LabelId,
|
||
},
|
||
For {
|
||
end_pos: crate::SourcePos,
|
||
var: HPlace,
|
||
ty: NumTy,
|
||
from: HExpr,
|
||
to: HExpr,
|
||
step: Option<HExpr>,
|
||
/// Versteckte Slots für Grenze/Schritt (Schritt nur wenn dynamisch).
|
||
limit_slot: VarSlot,
|
||
step_slot: Option<VarSlot>,
|
||
body: Vec<HStmt>,
|
||
exit_label: LabelId,
|
||
},
|
||
Goto(LabelId),
|
||
Gosub(LabelId),
|
||
OnGoto {
|
||
sel: HExpr,
|
||
gosub: bool,
|
||
targets: Vec<LabelId>,
|
||
},
|
||
ReturnGosub(Option<LabelId>),
|
||
Run {
|
||
target: Option<HExpr>,
|
||
string: bool,
|
||
},
|
||
/// Ereignis-Trap erklären (Sprachreferenz §8). `art` ist die Quelle
|
||
/// (0 KEY, 1 TIMER, 2 UEVENT, 3 SIGNAL), `index` ihre Kennung bzw. bei
|
||
/// `TIMER` das Intervall in Sekunden. `ziel = None` = `GOSUB 0`.
|
||
TrapDef {
|
||
art: u8,
|
||
index: HExpr,
|
||
ziel: Option<LabelId>,
|
||
},
|
||
/// `<quelle>(n) ON|OFF|STOP` — `zustand` 0 An, 1 Aus, 2 Gestoppt.
|
||
TrapSet {
|
||
art: u8,
|
||
index: HExpr,
|
||
zustand: u8,
|
||
},
|
||
/// `EVENT ON` / `EVENT OFF`.
|
||
EventSwitch(bool),
|
||
/// EXIT SUB/FUNCTION/DEF bzw. Rumpfende.
|
||
ExitProc,
|
||
CallSub {
|
||
proc: u16,
|
||
args: Vec<HArg>,
|
||
},
|
||
BuiltinStmt {
|
||
b: Builtin,
|
||
args: Vec<HExpr>,
|
||
},
|
||
OnError {
|
||
local: bool,
|
||
/// `None` = `GOTO 0` (deaktivieren).
|
||
target: Option<LabelId>,
|
||
},
|
||
OnErrorResumeNext {
|
||
local: bool,
|
||
},
|
||
Resume(HResume),
|
||
/// `ERROR n`.
|
||
RaiseError(HExpr),
|
||
/// `ERR = n` — Fehlercode setzen, ohne einen Fehler auszulösen.
|
||
SetErr(HExpr),
|
||
Read(Vec<HPlace>),
|
||
/// Ziel als Index in `HirModule::data` (0 = Anfang).
|
||
Restore(u32),
|
||
/// DIM/REDIM eines Arrays: Grenzen (lo, hi) je Dimension.
|
||
Dim {
|
||
slot: VarSlot,
|
||
elem: HTy,
|
||
dims: Vec<(HExpr, HExpr)>,
|
||
redim: bool,
|
||
},
|
||
Erase(Vec<VarSlot>),
|
||
End,
|
||
Stop,
|
||
System,
|
||
/// Dokumentiertes Feature einer späteren Phase → Laufzeitfehler 73.
|
||
Unsupported(&'static str),
|
||
}
|
||
|
||
/// Konstanter Literalwert eines Ausdrucks (z. B. FOR-STEP-Erkennung im
|
||
/// Codegen: konstanter Schritt braucht keinen versteckten Slot).
|
||
pub fn literal_value(e: &HExpr) -> Option<f64> {
|
||
match e {
|
||
HExpr::Int(v) => Some(*v as f64),
|
||
HExpr::Lng(v) => Some(*v as f64),
|
||
HExpr::Sng(v) => Some(*v as f64),
|
||
HExpr::Dbl(v) => Some(*v),
|
||
HExpr::Cur(v) => Some(*v as f64 / 10_000.0),
|
||
HExpr::Conv { arg, .. } => literal_value(arg),
|
||
HExpr::Neg { arg, .. } => literal_value(arg).map(|v| -v),
|
||
_ => None,
|
||
}
|
||
}
|