Phase 1 vollstaendig abgeschlossen; neue Vorgaben verankert

Frontend-Vervollstaendigung (keine offenen Punkte mehr in Phase 1):
- MID$-Anweisung, DEF-FN-Blockform, COMMON/SHARED/STATIC,
  LPRINT, VIEW PRINT, NAME AS, LOCK/UNLOCK, Ereignissteuerung
  (TIMER/KEY(n)/UEVENT ON|OFF|STOP), Metabefehle $INCLUDE/$STATIC/$DYNAMIC
- Komplette Datei-E/A-Grammatik: OPEN (beide Syntaxen, inkl. ISAM- und
  ACCESS/LOCK-Klauseln), CLOSE, FIELD, GET/PUT, LSET/RSET, WRITE, SEEK
- Semantik: UDT-Feldtypen, SHARED-Import von Modulvariablen,
  Konstantenfaltung (Invalid constant), OPTION BASE erfasst
- Non-Features konsistent zur Compile-Zeit abgewiesen: Hardware-Naehe,
  CHAIN, Grafik-Anweisungen, SOUND/PLAY ("Feature unavailable")
- 33 Frontend-Tests, Korpus-Meilenstein weiter gruen

Neue Vorgaben:
- Stufe-2-Ideen: SQLite/Embedded-SQL mit Record-Buffern; Runtime-
  Bibliotheken (z.B. crossterm) als BASIC-Bibliotheken
- Erster Kompatibilitaetstest: github.com/cout/vbdos (extern, wird nicht
  einvendort); binaer-.FRM-Konverter als Phase-4-Aufgabe (tbc convert-frm)
- Doku als Definition of Done: docs/ = Ist-Dokumentation, IDE-Hilfe
  rendert Markdown dynamisch zur Fenstergroesse (Phase-5-Aufgabe)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-02 09:30:44 +02:00
parent 333e794540
commit d77574d9de
6 changed files with 1059 additions and 165 deletions

View File

@@ -62,7 +62,7 @@ pub enum NumValue {
/// und -anweisungen sind KEINE Keywords, sondern Builtins der Semantik).
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Kw {
And, As, Call, Case, Const, Data, Declare, Def, DefCur, DefDbl, DefInt,
And, As, Call, Case, Common, Const, Data, Declare, Def, DefCur, DefDbl, DefInt,
DefLng, DefSng, DefStr, Dim, Do, Double, Else, ElseIf, End, Eqv, Erase,
Error, Exit, For, Function, Gosub, Goto, If, Imp, Input, Integer, Is,
Let, Line, Local, Long, Loop, Mod, Next, Not, On, Option, Or, Print,
@@ -77,7 +77,8 @@ fn keyword(upper: &str) -> Option<Kw> {
use Kw::*;
Some(match upper {
"AND" => And, "AS" => As, "CALL" => Call, "CASE" => Case,
"CONST" => Const, "CURRENCY" => Currency, "DATA" => Data,
"COMMON" => Common, "CONST" => Const, "CURRENCY" => Currency,
"DATA" => Data,
"DECLARE" => Declare, "DEF" => Def, "DEFCUR" => DefCur,
"DEFDBL" => DefDbl, "DEFINT" => DefInt, "DEFLNG" => DefLng,
"DEFSNG" => DefSng, "DEFSTR" => DefStr, "DIM" => Dim, "DO" => Do,
@@ -113,11 +114,41 @@ pub enum TokenKind {
Plus, Minus, Star, Slash, Backslash, Caret,
Eq, Ne, Lt, Le, Gt, Ge,
LParen, RParen, Comma, Semicolon, Colon, Hash,
/// Metabefehl `'$INCLUDE: 'datei''` (Pfad; leer = fehlerhafte Syntax).
MetaInclude(String),
/// Metabefehle `'$STATIC` / `'$DYNAMIC`.
MetaStatic,
MetaDynamic,
/// Ende einer logischen Zeile.
Eol,
Eof,
}
/// Prüft einen Kommentar auf Metabefehle (`$INCLUDE`, `$STATIC`, `$DYNAMIC`).
fn meta_token(rest: &[char]) -> Option<TokenKind> {
let s: String = rest.iter().collect();
let t = s.trim_start();
let up = t.to_uppercase();
if up.starts_with("$STATIC") {
return Some(TokenKind::MetaStatic);
}
if up.starts_with("$DYNAMIC") {
return Some(TokenKind::MetaDynamic);
}
if up.starts_with("$INCLUDE") {
if let Some(colon) = t.find(':') {
let after = t[colon + 1..].trim();
if let Some(stripped) = after.strip_prefix('\'') {
if let Some(end) = stripped.find('\'') {
return Some(TokenKind::MetaInclude(stripped[..end].to_string()));
}
}
}
return Some(TokenKind::MetaInclude(String::new()));
}
None
}
#[derive(Debug, Clone, PartialEq)]
pub struct Token {
pub kind: TokenKind,
@@ -171,7 +202,13 @@ pub fn lex(source: &str) -> LexOutput {
}
match c {
'\'' => break 'line, // Kommentar bis Zeilenende
'\'' => {
// Kommentar bis Zeilenende; ggf. Metabefehl
if let Some(tok) = meta_token(&chars[i + 1..]) {
tokens.push(Token { kind: tok, pos });
}
break 'line;
}
'"' => {
i += 1;
let mut s = String::new();
@@ -388,7 +425,11 @@ pub fn lex(source: &str) -> LexOutput {
if suffix.is_none() {
if let Some(kw) = keyword(&name) {
if kw == Kw::Rem {
break 'line; // REM: Rest ist Kommentar
// REM: Rest ist Kommentar; ggf. Metabefehl
if let Some(tok) = meta_token(&chars[i..]) {
tokens.push(Token { kind: tok, pos });
}
break 'line;
}
tokens.push(Token { kind: TokenKind::Kw(kw), pos });
continue;