Phase 0: Referenzdokumente, Fehlerkatalog, Testkorpus, Ratatui-Spike
Entscheidungen festgehalten: TBVM bestaetigt und eingebettet in die Executables; durchgaengig UTF-8 statt CP437 (dokumentierte Abweichung). - docs/: Sprachreferenz, Forms-Referenz, Dateiformate, TBVM-Design - tb-runtime::errors: klassischer Laufzeitfehler-Katalog (implementiert) - tb-ui::screen: 80x25-Unicode-Zellenpuffer mit 16-Farben-Abbildung, Scrollbereich (VIEW PRINT), Letterboxing; Ratatui-Widget + Tests - Spike: cargo run -p tb-ui --example spike (Farben, Unicode, Tasten, Maus) - tests/compat/: erste Referenzprogramme mit byte-genauer Sollausgabe Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,161 @@
|
||||
//! Laufzeitfehler: Codes und Meldungstexte kompatibel zum Vorbild
|
||||
//! (z. B. 6 = Overflow, 9 = Subscript out of range, 53 = File not found).
|
||||
//! Laufzeitfehler: Codes und Meldungstexte kompatibel zum Vorbild.
|
||||
//!
|
||||
//! Die Codes und (englischen) Meldungstexte entsprechen dem klassischen
|
||||
//! DOS-BASIC-Katalog; Programme prüfen `ERR` gegen diese Nummern, daher
|
||||
//! sind sie Teil des Kompatibilitätsvertrags. Nicht belegte Nummern
|
||||
//! liefern "Unprintable error" (wie im Vorbild bei `ERROR n`).
|
||||
|
||||
// Platzhalter — wird in Phase 3 ausgearbeitet (siehe PLAN.md)
|
||||
/// Ein Laufzeitfehler des Dialekts, identifiziert durch seinen klassischen Code.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct RuntimeError(pub u8);
|
||||
|
||||
impl RuntimeError {
|
||||
pub const NEXT_WITHOUT_FOR: Self = Self(1);
|
||||
pub const SYNTAX_ERROR: Self = Self(2);
|
||||
pub const RETURN_WITHOUT_GOSUB: Self = Self(3);
|
||||
pub const OUT_OF_DATA: Self = Self(4);
|
||||
pub const ILLEGAL_FUNCTION_CALL: Self = Self(5);
|
||||
pub const OVERFLOW: Self = Self(6);
|
||||
pub const OUT_OF_MEMORY: Self = Self(7);
|
||||
pub const LABEL_NOT_DEFINED: Self = Self(8);
|
||||
pub const SUBSCRIPT_OUT_OF_RANGE: Self = Self(9);
|
||||
pub const DUPLICATE_DEFINITION: Self = Self(10);
|
||||
pub const DIVISION_BY_ZERO: Self = Self(11);
|
||||
pub const ILLEGAL_IN_DIRECT_MODE: Self = Self(12);
|
||||
pub const TYPE_MISMATCH: Self = Self(13);
|
||||
pub const OUT_OF_STRING_SPACE: Self = Self(14);
|
||||
pub const STRING_TOO_COMPLEX: Self = Self(16);
|
||||
pub const CANNOT_CONTINUE: Self = Self(17);
|
||||
pub const FUNCTION_NOT_DEFINED: Self = Self(18);
|
||||
pub const NO_RESUME: Self = Self(19);
|
||||
pub const RESUME_WITHOUT_ERROR: Self = Self(20);
|
||||
pub const DEVICE_TIMEOUT: Self = Self(24);
|
||||
pub const DEVICE_FAULT: Self = Self(25);
|
||||
pub const FOR_WITHOUT_NEXT: Self = Self(26);
|
||||
pub const OUT_OF_PAPER: Self = Self(27);
|
||||
pub const WHILE_WITHOUT_WEND: Self = Self(29);
|
||||
pub const WEND_WITHOUT_WHILE: Self = Self(30);
|
||||
pub const DUPLICATE_LABEL: Self = Self(33);
|
||||
pub const SUBPROGRAM_NOT_DEFINED: Self = Self(35);
|
||||
pub const ARGUMENT_COUNT_MISMATCH: Self = Self(37);
|
||||
pub const ARRAY_NOT_DEFINED: Self = Self(38);
|
||||
pub const VARIABLE_REQUIRED: Self = Self(40);
|
||||
pub const FIELD_OVERFLOW: Self = Self(50);
|
||||
pub const INTERNAL_ERROR: Self = Self(51);
|
||||
pub const BAD_FILE_NAME_OR_NUMBER: Self = Self(52);
|
||||
pub const FILE_NOT_FOUND: Self = Self(53);
|
||||
pub const BAD_FILE_MODE: Self = Self(54);
|
||||
pub const FILE_ALREADY_OPEN: Self = Self(55);
|
||||
pub const FIELD_STATEMENT_ACTIVE: Self = Self(56);
|
||||
pub const DEVICE_IO_ERROR: Self = Self(57);
|
||||
pub const FILE_ALREADY_EXISTS: Self = Self(58);
|
||||
pub const BAD_RECORD_LENGTH: Self = Self(59);
|
||||
pub const DISK_FULL: Self = Self(61);
|
||||
pub const INPUT_PAST_END_OF_FILE: Self = Self(62);
|
||||
pub const BAD_RECORD_NUMBER: Self = Self(63);
|
||||
pub const BAD_FILE_NAME: Self = Self(64);
|
||||
pub const TOO_MANY_FILES: Self = Self(67);
|
||||
pub const DEVICE_UNAVAILABLE: Self = Self(68);
|
||||
pub const COMM_BUFFER_OVERFLOW: Self = Self(69);
|
||||
pub const PERMISSION_DENIED: Self = Self(70);
|
||||
pub const DISK_NOT_READY: Self = Self(71);
|
||||
pub const DISK_MEDIA_ERROR: Self = Self(72);
|
||||
pub const FEATURE_UNAVAILABLE: Self = Self(73);
|
||||
pub const RENAME_ACROSS_DISKS: Self = Self(74);
|
||||
pub const PATH_FILE_ACCESS_ERROR: Self = Self(75);
|
||||
pub const PATH_NOT_FOUND: Self = Self(76);
|
||||
|
||||
/// Klassischer Fehlercode für `ERR`.
|
||||
pub fn code(self) -> u8 {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// Meldungstext des Vorbilds; unbelegte Codes: "Unprintable error".
|
||||
pub fn message(self) -> &'static str {
|
||||
match self.0 {
|
||||
1 => "NEXT without FOR",
|
||||
2 => "Syntax error",
|
||||
3 => "RETURN without GOSUB",
|
||||
4 => "Out of DATA",
|
||||
5 => "Illegal function call",
|
||||
6 => "Overflow",
|
||||
7 => "Out of memory",
|
||||
8 => "Label not defined",
|
||||
9 => "Subscript out of range",
|
||||
10 => "Duplicate definition",
|
||||
11 => "Division by zero",
|
||||
12 => "Illegal in direct mode",
|
||||
13 => "Type mismatch",
|
||||
14 => "Out of string space",
|
||||
16 => "String formula too complex",
|
||||
17 => "Cannot continue",
|
||||
18 => "Function not defined",
|
||||
19 => "No RESUME",
|
||||
20 => "RESUME without error",
|
||||
24 => "Device timeout",
|
||||
25 => "Device fault",
|
||||
26 => "FOR without NEXT",
|
||||
27 => "Out of paper",
|
||||
29 => "WHILE without WEND",
|
||||
30 => "WEND without WHILE",
|
||||
33 => "Duplicate label",
|
||||
35 => "Subprogram not defined",
|
||||
37 => "Argument-count mismatch",
|
||||
38 => "Array not defined",
|
||||
40 => "Variable required",
|
||||
50 => "FIELD overflow",
|
||||
51 => "Internal error",
|
||||
52 => "Bad file name or number",
|
||||
53 => "File not found",
|
||||
54 => "Bad file mode",
|
||||
55 => "File already open",
|
||||
56 => "FIELD statement active",
|
||||
57 => "Device I/O error",
|
||||
58 => "File already exists",
|
||||
59 => "Bad record length",
|
||||
61 => "Disk full",
|
||||
62 => "Input past end of file",
|
||||
63 => "Bad record number",
|
||||
64 => "Bad file name",
|
||||
67 => "Too many files",
|
||||
68 => "Device unavailable",
|
||||
69 => "Communication-buffer overflow",
|
||||
70 => "Permission denied",
|
||||
71 => "Disk not ready",
|
||||
72 => "Disk-media error",
|
||||
73 => "Feature unavailable",
|
||||
74 => "Rename across disks",
|
||||
75 => "Path/File access error",
|
||||
76 => "Path not found",
|
||||
_ => "Unprintable error",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for RuntimeError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.message())
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for RuntimeError {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn bekannte_codes() {
|
||||
assert_eq!(RuntimeError::OVERFLOW.code(), 6);
|
||||
assert_eq!(RuntimeError::OVERFLOW.message(), "Overflow");
|
||||
assert_eq!(RuntimeError::SUBSCRIPT_OUT_OF_RANGE.code(), 9);
|
||||
assert_eq!(RuntimeError::FILE_NOT_FOUND.message(), "File not found");
|
||||
assert_eq!(RuntimeError::FEATURE_UNAVAILABLE.code(), 73);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unbelegte_codes_sind_unprintable() {
|
||||
assert_eq!(RuntimeError(15).message(), "Unprintable error");
|
||||
assert_eq!(RuntimeError(200).message(), "Unprintable error");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user