Phase 3 (Kern): Laufzeitbibliothek, Bildschirm und Datei-E/A
Setzt den OpenSpec-Change phase-3-runtime-bildschirm um (77/77 Aufgaben). Abdeckung laut Inventar: 195 implementiert, 37 offen, 53 Non-Feature. Vollstaendigkeits-Inventar - docs/inventar.md mit 285 Eintraegen aus den Themenlisten von bas7advr.hlp und qb45advr.hlp, je mit Status und Fundstelle - crates/tb-frontend/tests/inventar.rs haelt die Tabelle in beide Richtungen gegen den Code; der Abdeckungsstand kann nicht veralten Bruchschritt (Puffer, Host, Korpus) - Cell/TextScreen ziehen ratatui-frei nach tb-runtime::screen; tb-ui behaelt Farbabbildung, Widget und den neuen Terminal-Host - Host wechselt vom Zeichenstrom auf Anzeige des Zellenpuffers plus Ereignisse (Taste, Groessenaenderung, Abbruch) - Korpusvergleich auf getrimmten Bildschirm-Snapshot; die acht bestehenden Sollausgaben blieben dabei unveraendert Groessenunabhaengigkeit - 80x25 ist nirgends mehr eine feste Grenze; Groessenaenderungen waehrend der Ausfuehrung werden zugestellt (Inhalt oben links erhalten, Cursor und VIEW PRINT geklemmt) - Korpusnachweis: dasselbe Programm bei 80x25 und 120x40 mit je eigener Sollausgabe, plus ein Programm mit Groessenwechsel mitten im Lauf Bibliothek - Breite Unicode-Zeichen belegen zwei Zellen (Cursor, POS, Randumbruch) - Bildschirm: CLS, COLOR, LOCATE, WIDTH, VIEW PRINT, SCREEN, CSRLIN, POS - Tastatur: INKEY$, INPUT$, Funktionstasten-Makros (KEY n / LIST / ON) - PRINT USING, LPRINT USING, FORMAT$, SetFormatCC - Mathematik mit kompatiblem PRNG (gleiche Saat, gleiche Folge) - Datum und Zeit mit Serienwerten ab 1899 - Finanzmathematik: FV#, PV#, Pmt#, IPmt#, PPmt#, NPer#, Rate#, NPV#, IRR#, MIRR#, SLN#, SYD#, DDB# - Datei-E/A: sequenziell, RANDOM (Recordpuffer und UDT-Variablen), BINARY, FIELD/LSET/RSET, Statusfunktionen, Dateisystem, MK$/CV - System: ENVIRON, FRE, CLEAR, TRON/TROFF, STACK, ERDEV, ERR-Anweisung Altlasten aus Phase 2 - ON ERROR GOTO auf Modulebene ist aus Prozeduren ansprechbar (prozeduruebergreifender Fixup im Codegenerator) - DATA behaelt seinen Rohtext (Gross-/Kleinschreibung, innerer Leerraum) - Die vier TODO-verify-Zellen der Konvertierungsmatrix sind aufgeloest Vom Inventar aufgedeckte Fehler - Zwoelf Non-Features wies der Compiler entgegen der Phase-1-Spec nicht ab (CALLS, SSEG, POINT, VIEW, COM, PEN, STRIG, STICK, die String*-Routinen, LINE und OPEN "COMn:") -- geschlossen - Drei Gruppen fehlten im urspruenglichen Umfang: Finanzmathematik, Record-Konvertierung, Rest der Dateisystemfunktionen - bas7advr.hlp allein ist keine vollstaendige Quelle; das Inventar bildet die Vereinigung mit qb45advr.hlp Neue Changes - phase-3-isam: schliesst Phase 3 ab (Speicherschicht redb) - phase-3-ortszeit: zieht die UTC-Abweichung zurueck Neue Abhaengigkeit: unicode-width. Dokumentiert: sprachreferenz.md und tbvm-design.md sind TODO-frei, docs/bibliothek.md neu, PLAN.md fortgeschrieben. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -19,6 +19,7 @@ pub fn compile(hir: &HirModule) -> CompiledModule {
|
||||
strings: Vec::new(),
|
||||
string_ids: HashMap::new(),
|
||||
jump_tables: Vec::new(),
|
||||
modul_label_pc: Vec::new(),
|
||||
};
|
||||
let mut procs = Vec::new();
|
||||
for proc in &hir.procs {
|
||||
@@ -74,6 +75,10 @@ struct Codegen {
|
||||
strings: Vec<Rc<str>>,
|
||||
string_ids: HashMap<String, u16>,
|
||||
jump_tables: Vec<Vec<u32>>,
|
||||
/// Sprungziele des Modulrumpfs (Prozedur 0). Ein modulweites
|
||||
/// `ON ERROR GOTO` aus einer Prozedur zeigt dorthin; da Prozedur 0
|
||||
/// zuerst übersetzt wird, stehen die Positionen rechtzeitig fest.
|
||||
modul_label_pc: Vec<Option<u32>>,
|
||||
}
|
||||
|
||||
struct ProcCtx {
|
||||
@@ -137,9 +142,26 @@ impl Codegen {
|
||||
// Rumpfende
|
||||
self.emit_proc_exit(&mut ctx, proc);
|
||||
|
||||
// Prozedur 0 (Modulrumpf) gibt ihre Sprungziele weiter.
|
||||
if proc.kind == hir::HProcKind::Main {
|
||||
self.modul_label_pc = ctx.label_pc.clone();
|
||||
}
|
||||
|
||||
// Fixups patchen
|
||||
for (idx, label) in std::mem::take(&mut ctx.fixups) {
|
||||
let pc = ctx.label_pc[label as usize].expect("Label ohne Position");
|
||||
// Modulweites `ON ERROR GOTO` in einer Prozedur: das Label lebt
|
||||
// im Modulrumpf, nicht im eigenen.
|
||||
let modulweit = proc.kind != hir::HProcKind::Main
|
||||
&& matches!(ctx.code[idx], Instr::OnErrorGoto(_));
|
||||
let pc = if modulweit {
|
||||
self.modul_label_pc
|
||||
.get(label as usize)
|
||||
.copied()
|
||||
.flatten()
|
||||
.expect("Modul-Label ohne Position")
|
||||
} else {
|
||||
ctx.label_pc[label as usize].expect("Label ohne Position")
|
||||
};
|
||||
match &mut ctx.code[idx] {
|
||||
Instr::Jump(t)
|
||||
| Instr::JumpIfFalse(t)
|
||||
@@ -224,7 +246,57 @@ impl Codegen {
|
||||
ctx.emit(Instr::CallBuiltin(ids::PRINT_NEWLINE, 0));
|
||||
}
|
||||
}
|
||||
HStmtKind::Input { line_mode, prompt, question, targets } => {
|
||||
HStmtKind::SetErr(e) => {
|
||||
self.expr(ctx, e);
|
||||
ctx.emit(Instr::SetErr);
|
||||
}
|
||||
HStmtKind::Field { file, fields } => {
|
||||
self.expr(ctx, file);
|
||||
for (len, place) in fields {
|
||||
self.expr(ctx, len);
|
||||
self.make_ref(ctx, place);
|
||||
}
|
||||
ctx.emit(Instr::Field(fields.len() as u8));
|
||||
}
|
||||
HStmtKind::LsetRset { rset, target, value } => {
|
||||
self.make_ref(ctx, target);
|
||||
self.expr(ctx, value);
|
||||
ctx.emit(Instr::LsetRset(*rset));
|
||||
}
|
||||
HStmtKind::GetPut { put, file, recnum, var } => {
|
||||
self.expr(ctx, file);
|
||||
if let Some(r) = recnum {
|
||||
self.expr(ctx, r);
|
||||
}
|
||||
let (art, zusatz) = match var {
|
||||
None => (0u8, 0u16),
|
||||
Some(v) => {
|
||||
self.make_ref(ctx, v);
|
||||
match &v.ty {
|
||||
HTy::Num(NumTy::Int) => (1, 0),
|
||||
HTy::Num(NumTy::Lng) => (2, 0),
|
||||
HTy::Num(NumTy::Sng) => (3, 0),
|
||||
HTy::Num(NumTy::Dbl) => (4, 0),
|
||||
HTy::Num(NumTy::Cur) => (5, 0),
|
||||
HTy::FixedStr(n) => (6, *n as u16),
|
||||
HTy::Udt(i) => (7, *i),
|
||||
// Variable Strings: Länge erst zur Laufzeit.
|
||||
HTy::Str => (8, 0),
|
||||
}
|
||||
}
|
||||
};
|
||||
ctx.emit(Instr::GetPut(*put, recnum.is_some(), art, zusatz));
|
||||
}
|
||||
HStmtKind::Input { file, line_mode, prompt, question, targets } => {
|
||||
if let Some(f) = file {
|
||||
// Dateinummer zuerst, dann die Referenzen darüber.
|
||||
self.expr(ctx, f);
|
||||
for t in targets {
|
||||
self.make_ref(ctx, t);
|
||||
}
|
||||
ctx.emit(Instr::InputFile(targets.len() as u8, *line_mode));
|
||||
return;
|
||||
}
|
||||
for t in targets {
|
||||
self.make_ref(ctx, t);
|
||||
}
|
||||
@@ -901,9 +973,90 @@ fn builtin_id(b: Builtin) -> u16 {
|
||||
Builtin::PrintTab => ids::PRINT_TAB,
|
||||
Builtin::PrintSpc => ids::PRINT_SPC,
|
||||
Builtin::PrintNewline => ids::PRINT_NEWLINE,
|
||||
Builtin::PrintUsing => ids::PRINT_USING,
|
||||
Builtin::FormatS => ids::FORMAT_S,
|
||||
Builtin::SetFormatCc => ids::SET_FORMAT_CC,
|
||||
Builtin::Cls => ids::CLS,
|
||||
Builtin::Color => ids::COLOR,
|
||||
Builtin::Locate => ids::LOCATE,
|
||||
Builtin::Width => ids::WIDTH,
|
||||
Builtin::ViewPrint => ids::VIEW_PRINT,
|
||||
Builtin::ScreenStmt => ids::SCREEN_STMT,
|
||||
Builtin::KeyAssign => ids::KEY_ASSIGN,
|
||||
Builtin::KeyList => ids::KEY_LIST,
|
||||
Builtin::KeyDisplay => ids::KEY_DISPLAY,
|
||||
Builtin::Csrlin => ids::CSRLIN,
|
||||
Builtin::PosFn => ids::POS_FN,
|
||||
Builtin::ScreenFn => ids::SCREEN_FN,
|
||||
Builtin::InkeyS => ids::INKEY_S,
|
||||
Builtin::InputS => ids::INPUT_S,
|
||||
Builtin::EnvironS => ids::ENVIRON_S,
|
||||
Builtin::EnvironSet => ids::ENVIRON_SET,
|
||||
Builtin::Fre => ids::FRE,
|
||||
Builtin::Clear => ids::CLEAR,
|
||||
Builtin::Tron => ids::TRON,
|
||||
Builtin::Troff => ids::TROFF,
|
||||
Builtin::StackFn => ids::STACK_FN,
|
||||
Builtin::StackStmt => ids::STACK_STMT,
|
||||
Builtin::Erdev => ids::ERDEV,
|
||||
Builtin::ErdevS => ids::ERDEV_S,
|
||||
Builtin::Open => ids::OPEN,
|
||||
Builtin::Close => ids::CLOSE,
|
||||
Builtin::CloseAll => ids::CLOSE_ALL,
|
||||
Builtin::PrintZiel => ids::PRINT_ZIEL,
|
||||
Builtin::WriteFile => ids::WRITE_FILE,
|
||||
Builtin::EofF => ids::EOF_F,
|
||||
Builtin::LofF => ids::LOF_F,
|
||||
Builtin::LocF => ids::LOC_F,
|
||||
Builtin::SeekF => ids::SEEK_F,
|
||||
Builtin::SeekStmt => ids::SEEK_STMT,
|
||||
Builtin::Freefile => ids::FREEFILE,
|
||||
Builtin::Fileattr => ids::FILEATTR,
|
||||
Builtin::LockStmt => ids::LOCK_STMT,
|
||||
Builtin::Kill => ids::KILL,
|
||||
Builtin::NameStmt => ids::NAME_STMT,
|
||||
Builtin::Files => ids::FILES,
|
||||
Builtin::Chdir => ids::CHDIR,
|
||||
Builtin::Chdrive => ids::CHDRIVE,
|
||||
Builtin::Mkdir => ids::MKDIR,
|
||||
Builtin::Rmdir => ids::RMDIR,
|
||||
Builtin::CurdirS => ids::CURDIR_S,
|
||||
Builtin::DirS => ids::DIR_S,
|
||||
Builtin::Lpos => ids::LPOS,
|
||||
Builtin::ShellStmt => ids::SHELL_STMT,
|
||||
Builtin::ShellFn => ids::SHELL_FN,
|
||||
Builtin::MkS => ids::MK_S,
|
||||
Builtin::CvF => ids::CV_F,
|
||||
Builtin::Fv => ids::FV,
|
||||
Builtin::Pv => ids::PV,
|
||||
Builtin::Pmt => ids::PMT,
|
||||
Builtin::NPer => ids::NPER,
|
||||
Builtin::IPmt => ids::IPMT,
|
||||
Builtin::PPmt => ids::PPMT,
|
||||
Builtin::Rate => ids::RATE,
|
||||
Builtin::Npv => ids::NPV,
|
||||
Builtin::Irr => ids::IRR,
|
||||
Builtin::Mirr => ids::MIRR,
|
||||
Builtin::Sln => ids::SLN,
|
||||
Builtin::Syd => ids::SYD,
|
||||
Builtin::Ddb => ids::DDB,
|
||||
Builtin::Timer => ids::TIMER,
|
||||
Builtin::DateS => ids::DATE_S,
|
||||
Builtin::TimeS => ids::TIME_S,
|
||||
Builtin::DateSet => ids::DATE_SET,
|
||||
Builtin::TimeSet => ids::TIME_SET,
|
||||
Builtin::Now => ids::NOW,
|
||||
Builtin::DateSerial => ids::DATE_SERIAL,
|
||||
Builtin::TimeSerial => ids::TIME_SERIAL,
|
||||
Builtin::DateValue => ids::DATE_VALUE,
|
||||
Builtin::TimeValue => ids::TIME_VALUE,
|
||||
Builtin::DayF => ids::DAY_F,
|
||||
Builtin::MonthF => ids::MONTH_F,
|
||||
Builtin::YearF => ids::YEAR_F,
|
||||
Builtin::WeekdayF => ids::WEEKDAY_F,
|
||||
Builtin::HourF => ids::HOUR_F,
|
||||
Builtin::MinuteF => ids::MINUTE_F,
|
||||
Builtin::SecondF => ids::SECOND_F,
|
||||
Builtin::CommandS => ids::COMMAND_S,
|
||||
Builtin::Doevents => ids::DOEVENTS,
|
||||
Builtin::Sleep => ids::SLEEP,
|
||||
|
||||
Reference in New Issue
Block a user