Setzt den OpenSpec-Change phase-3-isam um (49/49 Aufgaben) und schliesst damit Phase 3 ab. Alle 22 ISAM-Elemente des Inventars sind implementiert; der Abdeckungsstand steigt auf 217 implementiert / 15 offen / 53 Non-Feature. Frontend - ISAM-Anweisungen als eigener Zweig im Parser: NAME [#]n [, arg ...], Sonderform ROLLBACK ALL ueber eine Sentinel-Kennung - 22 Signaturen in builtin_stmt/builtin_fn, neue Argumentart R (Satzvariable eines benutzerdefinierten Typs) - Satzargumente von INSERT/RETRIEVE/UPDATE werden gegen den Typ der Dateinummer geprueft, sofern beide literal bekannt sind - Die Unsupported-Absenkung von OPEN ... FOR ISAM entfaellt Speicherschicht (tb-runtime::isam, einziger Ort mit redb) - Je Tabelle eine Satztabelle satz-id -> Satzbytes, je Index eine Tabelle Schluesselbytes -> satz-id. Satz-IDs sind monoton und werden nie wiederverwendet: der Cursor merkt sich eine ID, eine neu vergebene koennte still auf einen fremden Satz zeigen - Satzbytes entstehen mit fileio::wert_schreiben/wert_lesen, also mit den Recordpuffern und der UTF-32-Festtextkodierung aus datei-eio - Ordnungserhaltende Schluesselkodierung je Spaltentyp; Text als UTF-8, dessen Bytereihenfolge die Codepoint-Reihenfolge ist und damit dieselbe Ordnung wie CmpStr der VM. Ein Eigenschaftstest ueber 5000 zufaellige Wertepaare je Typ haelt fest, dass der Bytevergleich dem fachlichen Vergleich entspricht -- eine Ordnungsverletzung faellt sonst erst bei bestimmten Datenwerten auf - Cursor als Wert (aktiver Index, letzte Satz-ID, unpositioniert), nicht als gehaltener Iterator: er ueberlebt Satzaenderungen und Transaktionsgrenzen - Sicherungspunkte ueber ein eigenes Ruecknahmeprotokoll; ROLLBACK ALL bricht die Bibliothekstransaktion direkt ab Semantik durchweg aus der Original-Hilfe - Die Argumentformen stammen erstmals aus den Einzelseiten, nicht nur aus der Themenliste. Das korrigierte eine Annahme des Entwurfs: die Spaltenliste von CREATEINDEX ist keine Zeichenkette mit Trennzeichen, sondern eine Folge einzelner Stringargumente - SEEKEQ mit unvollstaendigem Schluessel schlaegt immer fehl, SEEKGT mit Teilschluessel positioniert wie SEEKGE - Nach SETINDEX ist der erste Satz der neuen Ordnung aktuell, nach DELETE der folgende - Suche ueber den NULL-Index meldet 87, nicht 83; damit hat jeder Code von 81 bis 89 einen Ausloeser - Einzige Erweiterung: ein - vor dem Spaltennamen ordnet absteigend. Die Original-Hilfe kennt bei CREATEINDEX keine Sortierrichtung, die Anforderung verlangt sie. Kollisionsfrei, weil ein TYPE-Feldname nie mit - beginnen kann CLOSE beendet keine Transaktion - Erst schrieb es sie fest, womit das Schliessen irgendeiner Dateinummer die Transaktion aller anderen mit beendete und ein folgendes ROLLBACK ALL ins Leere lief. Ausstehende Aenderungen sind ohnehin festgeschrieben, weil jede Operation ausserhalb einer Transaktion fuer sich eine ist - Ueber das Ende entscheiden allein COMMITTRANS und ROLLBACK ALL; eine beim Programmende offene Transaktion verfaellt, in beiden Wegen gleich SETMEM und Fehler 89 - Ohne DOS-Speichermodell ist SETMEM die Obergrenze des ISAM-Puffers (Vorgabe 65536 Bytes); ohne echte Grenze waere Code 89 nie erreichbar und die Anweisung eine Attrappe Tests - 18 Einheitentests (Kodierung, Formatversion, Satz-IDs, Roundtrip ueber alle Feldtypen, Indexordnung, Cursor, Protokoll) - 6 Korpusprogramme: Tabellen, Indizes, Cursor, Saetze, Transaktionen, Puffer und Vergleichsordnung -- je in einem temporaeren Arbeits- verzeichnis, der Projektbaum bleibt sauber - Der VM-Test zu Fehler 73 prueft ISAM nicht mehr, sondern SETUEVENT; dafuer belegt ein neuer Test, dass OPEN ... FOR ISAM arbeitet Dokumentation: Datenbankdateiformat in dateiformate.md, Abschnitt 10a samt Sortierordnung und SETMEM-Abweichung in sprachreferenz.md, ISAM-Abschnitt in bibliothek.md, Inventar auf implementiert, PLAN.md schaerft die Leitplanke Referenzverhalten -- sie gilt auch gegenueber den eigenen Spezifikationen. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
485 lines
26 KiB
Markdown
485 lines
26 KiB
Markdown
# Sprachreferenz Terminal Basic
|
||
|
||
Rekonstruierte Referenz des Dialekts (DOS-BASIC, Stand 1992: prozedurale
|
||
BASIC-Familie mit Forms-Erweiterung). Dieses Dokument ist die verbindliche
|
||
Grundlage für Frontend und Runtime. Offene Detailfragen sind ausgeräumt
|
||
(Stand 2026-09-04); wo die Original-Dokumentation schweigt, steht an Ort
|
||
und Stelle eine datierte Entscheidung mit Begründung.
|
||
|
||
**Primärquellen:** Original-Hilfedateien des Vorbilds als HTML unter
|
||
https://dos-help.soulsphere.org/ (`qck.hlp` Quick Reference, `advr.hlp`
|
||
Details, `ener.hlp` Fehlermeldungen; Vergleich PDS 7.1: `bas7qck.hlp`)
|
||
sowie das Original-README der Professional Edition. Aussagen ohne
|
||
ausgewiesene Entscheidung sind daraus belegt; die Themenlisten von
|
||
`bas7advr.hlp` und `qb45advr.hlp` bilden zusammen das
|
||
Vollständigkeits-Inventar (docs/inventar.md).
|
||
|
||
**Guiding Principle:** Die Original-Dokumentation führt. Jedes dort
|
||
dokumentierte Sprachelement wird unterstützt oder — nur nach expliziter
|
||
Non-Feature-Entscheidung des Projektinhabers — mit einer klaren
|
||
Fehlermeldung abgewiesen. **Bewusste Abweichungen und Non-Features**
|
||
stehen am Ende dieses Dokuments; nur diese Liste rechtfertigt Abweisungen.
|
||
|
||
---
|
||
|
||
## 1. Lexik
|
||
|
||
- **Zeilenorientiert.** Eine logische Zeile enthält eine oder mehrere
|
||
Anweisungen, getrennt durch `:`. **Zeilenfortsetzung:** `_` als letztes
|
||
Zeichen (mit vorangehendem Leerzeichen/Tab) setzt die Zeile fort — im
|
||
Vorbild für extern erstellten Code gedacht (der IDE-Editor fügt
|
||
fortgesetzte Zeilen beim Laden zusammen); Terminal Basic akzeptiert es
|
||
überall.
|
||
- **Zeilennummern** sind optional und wirken als Labels. Alphanumerische
|
||
**Labels** enden mit `:` am Zeilenanfang (`Fehler:`).
|
||
- **Kommentare:** `REM` (ganze Anweisung) und `'` (bis Zeilenende).
|
||
`REM`/`'` am Zeilenanfang mit `$STATIC`/`$DYNAMIC`/`$INCLUDE: 'datei'`
|
||
sind **Metabefehle**.
|
||
- **Bezeichner:** Buchstabe, dann Buchstaben/Ziffern/`.`/`_`, max. 40
|
||
Zeichen, case-insensitiv. Optionales Typ-Suffix als letztes Zeichen.
|
||
(`_` in Bezeichnern ist eine dokumentierte Neuerung des Vorbilds.)
|
||
- **Typ-Suffixe:** `%` INTEGER · `&` LONG · `!` SINGLE · `#` DOUBLE ·
|
||
`$` STRING · `@` CURRENCY. `name`, `name%`, `name$` sind
|
||
**verschiedene Variablen**.
|
||
- **Keywords** sind reserviert und case-insensitiv; die IDE normalisiert
|
||
auf Großschreibung.
|
||
- **Numerische Literale:** dezimal (`123`, `1.5`, `1.5E3`, `1D3` für DOUBLE),
|
||
hexadezimal `&HFF`, oktal `&O777`; Suffixe erzwingen den Typ (`10%`, `10&`,
|
||
`1.5!`, `1.5#`, `2.5@`). Ohne Suffix gilt: Ganzzahl im INTEGER-Bereich →
|
||
INTEGER; Hex/Oktal ohne `&`-Suffix → 16 Bit (INTEGER), mit `&` → LONG;
|
||
`E`-Exponent → SINGLE, `D`-Exponent → DOUBLE; Dezimalpunkt-Wert:
|
||
**Entscheidung (2026-09-02):** bis 7 signifikante Stellen → SINGLE,
|
||
darüber → DOUBLE. (Die Original-Hilfe nennt widersprüchlich eine
|
||
15-Stellen-Schwelle, die SINGLE-Präzision verlöre; wir legen die Schwelle
|
||
auf die SINGLE-Genauigkeit — präzisionserhaltende Auslegung.)
|
||
Ganzzahl außerhalb des LONG-Bereichs wird Gleitkommawert (kein Fehler).
|
||
- **String-Literale:** `"…"`; doppeltes `""` ergibt ein Anführungszeichen.
|
||
|
||
## 2. Typsystem
|
||
|
||
| Typ | Suffix | Repräsentation | Wertebereich |
|
||
|---|---|---|---|
|
||
| INTEGER | `%` | i16 | −32 768 … 32 767 |
|
||
| LONG | `&` | i32 | −2 147 483 648 … 2 147 483 647 |
|
||
| SINGLE | `!` | f32 | ±3.4E38 |
|
||
| DOUBLE | `#` | f64 | ±1.8E308 |
|
||
| STRING | `$` | dynamischer Unicode-String | Länge 0 … 32 767 Zeichen |
|
||
| STRING * n | — | fester String, n Zeichen | nur in `TYPE`/`DIM` |
|
||
| CURRENCY | `@` | i64, Festkomma ×10 000 | ±922 337 203 685 477.5807 |
|
||
|
||
- **Benutzerdefinierte Typen:** `TYPE name … END TYPE` mit Elementen fester
|
||
Größe (numerische Typen, `STRING * n`, verschachtelte TYPEs). Kein
|
||
dynamischer STRING in TYPE.
|
||
- **Implizite Deklaration:** Erstverwendung deklariert die Variable. Ohne
|
||
Suffix gilt der Standardtyp SINGLE, änderbar per
|
||
`DEFINT/DEFLNG/DEFSNG/DEFDBL/DEFSTR/DEFCUR a–z` (buchstabenbereichsweise,
|
||
wirkt pro Modul/Prozedur ab Deklaration).
|
||
- `OPTION EXPLICIT` **existiert** (Neuerung des Vorbilds gegenüber der
|
||
QB/PDS-Familie): erzwingt Deklaration aller Variablen (über COMMON,
|
||
CONST, DEF FN, DIM, FUNCTION, REDIM, SHARED, STATIC oder SUB); gilt
|
||
auch für das Direktfenster.
|
||
- **Arrays:** `DIM a(10)`, `DIM a(1 TO 10, 0 TO 5)`. Untergrenze standardmäßig
|
||
0, per `OPTION BASE 1` änderbar. `$STATIC`/`$DYNAMIC` bzw. Kontext bestimmen
|
||
statisch/dynamisch; `REDIM` (dynamisch, löscht Inhalt), `ERASE`
|
||
(reinitialisiert statisch / gibt dynamisch frei). Max. **60 Dimensionen**,
|
||
Indizes −32 767 … 32 767 (mit `TO`). `LBOUND`/`UBOUND` liefern Grenzen.
|
||
(DOS-Speichergrenzen der Arrays — 64K/128K-Regeln — übernehmen wir nicht.)
|
||
- **Konvertierung:** implizit zwischen numerischen Typen mit Rundung
|
||
(Banker's Rounding bei `CINT`/`CLNG` und Zuweisung an Ganzzahl); Überlauf
|
||
→ Fehler 6. Keine implizite Konvertierung Zahl ↔ String (Fehler 13,
|
||
Type mismatch). Die vollständige Konvertierungsmatrix (Ergebnistypen
|
||
der Operatoren, Rundungs- und Überlaufregeln, Textdarstellung) steht
|
||
in docs/tbvm-design.md und ist über `tests/compat/konvertierung.bas`
|
||
ausführbar verankert (Phase 2, 2026-09-02).
|
||
|
||
## 3. Deklarationen und Sichtbarkeit
|
||
|
||
- `DIM [SHARED] var[(dims)] [AS typ]` — `AS`-Klausel: INTEGER, LONG, SINGLE,
|
||
DOUBLE, STRING, STRING * n, CURRENCY, benutzerdefinierter Typ.
|
||
- `COMMON [SHARED] [/blockname/] liste` — modulübergreifend (Kette
|
||
CHAIN-kompatibel im Vorbild; ohne `CHAIN` bleibt `COMMON` die
|
||
Verknüpfung mehrerer Module eines Projekts — geklärt mit der
|
||
Projektverwaltung in Phase 5).
|
||
- `SHARED` (in Prozedur): Zugriff auf Modulebene-Variablen.
|
||
- `STATIC` (in Prozedur): Variablen behalten Werte zwischen Aufrufen;
|
||
`STATIC`-Attribut an `SUB`/`FUNCTION` macht alle lokalen Variablen statisch.
|
||
- `CONST name = ausdruck` — Konstanten (konstante Ausdrücke zur Compilezeit).
|
||
- `DECLARE SUB/FUNCTION name (parameter)` — Prototyp; die IDE erzeugt sie
|
||
automatisch beim Speichern.
|
||
|
||
## 4. Operatoren (nach Priorität, hoch → niedrig)
|
||
|
||
1. `^` (Potenz)
|
||
2. unäres `-`
|
||
3. `*`, `/` (Fließkommadivision)
|
||
4. `\` (Ganzzahldivision; Operanden werden gerundet auf INTEGER/LONG)
|
||
5. `MOD` (Ganzzahlrest, Vorzeichen wie Dividend)
|
||
6. `+`, `-` (`+` auch String-Verkettung)
|
||
7. Vergleiche `= <> < > <= >=` (Zahlen und Strings; Strings
|
||
codepoint-weise — Abweichung, s. u.)
|
||
8. `NOT`
|
||
9. `AND`
|
||
10. `OR`
|
||
11. `XOR`
|
||
12. `EQV`
|
||
13. `IMP`
|
||
|
||
Logische Operatoren sind **bitweise** auf Ganzzahlen; Vergleichsergebnis ist
|
||
INTEGER −1 (wahr) / 0 (falsch).
|
||
|
||
## 5. Kontrollfluss
|
||
|
||
- `IF b THEN … [ELSE …]` (einzeilig) und Block-`IF … THEN / ELSEIF / ELSE /
|
||
END IF`
|
||
- `SELECT CASE ausdruck` mit `CASE wert`, `CASE a TO b`, `CASE IS >= x`,
|
||
`CASE ELSE`
|
||
- `FOR i = a TO b [STEP s] … NEXT [i]` (Grenzen werden einmal ausgewertet;
|
||
Schleifenvariable numerisch)
|
||
- `DO [WHILE|UNTIL b] … LOOP [WHILE|UNTIL b]`, `WHILE … WEND`
|
||
- `EXIT FOR / EXIT DO / EXIT SUB / EXIT FUNCTION / EXIT DEF`
|
||
- `GOTO ziel`, `GOSUB ziel` / `RETURN [ziel]`
|
||
- `ON n GOTO liste`, `ON n GOSUB liste` (berechneter Sprung, 1-basiert;
|
||
0 oder > Anzahl: kein Sprung; negativ/>255: Fehler 5)
|
||
- `END` (Programmende), `STOP` (Unterbrechung → im IDE-Kontext Debugger),
|
||
`SYSTEM` (Programmende, im Vorbild „zurück zu DOS").
|
||
Runner-Verhalten (Entscheidung 2026-09-02): außerhalb der IDE beendet
|
||
`STOP` das Programm mit der Meldung `STOP in line n` und Exit-Code ≠ 0;
|
||
`END`/`SYSTEM`/Programmende liefern Exit-Code 0. `CONT` existiert nur
|
||
als Direktmodus-Befehl der IDE (Phase 5) und ist nicht kompilierbar.
|
||
- `SLEEP [sekunden]`, `DO EVENTS`/`DOEVENTS` (Ereignisse verarbeiten —
|
||
zentral für Forms)
|
||
|
||
## 6. Prozeduren
|
||
|
||
- `SUB name (p1 AS t, p2(), …) [STATIC] … END SUB`; Aufruf `CALL name(args)`
|
||
oder `name args` (ohne Klammern).
|
||
- `FUNCTION name (…) [STATIC] … name = wert … END FUNCTION`; Typ über Suffix
|
||
oder `DEF…`-Regel.
|
||
- **Parameterübergabe standardmäßig BYREF.** Klammern um ein Argument
|
||
(`CALL f((x))`) erzwingen Wertübergabe. `BYVAL` nur in `DECLARE` für
|
||
externe Routinen (entfällt bei uns; `BYVAL` allgemein zu erlauben wäre
|
||
eine Erweiterung über den Kernstandard hinaus → Stufe 2, PLAN.md).
|
||
- Arrays werden mit `name()` übergeben, TYPEs BYREF.
|
||
- Rekursion erlaubt (außer bei `STATIC`-Semantik-Konflikten).
|
||
- `DEF FNname (args) = ausdruck` und Block-`DEF FN … END DEF` (mit
|
||
`EXIT DEF`); Aufruf `FNname(…)`. Nur auf Modulebene definier- und
|
||
nutzbar, **nicht rekursiv**, Argumente by value, keine Arrays/Records/
|
||
Fixed-Strings als Argumente.
|
||
|
||
## 7. Fehlerbehandlung
|
||
|
||
- Vollständige Syntax: `ON [LOCAL] ERROR {GOTO zeile/label | RESUME NEXT |
|
||
GOTO 0}`. `LOCAL` installiert einen **prozedurlokalen** Handler;
|
||
ohne `LOCAL` gilt der Handler modulweit. `GOTO 0` deaktiviert (in einem
|
||
aktiven Handler: Fehler weiterreichen → Programmabbruch).
|
||
- Im Handler: `RESUME` (fehlerauslösende Anweisung wiederholen),
|
||
`RESUME NEXT`, `RESUME zeile/label`.
|
||
- `ERR` (Code), `ERL` (Zeilennummer, nur numerische Zeilennummern!),
|
||
`ERROR n` (Fehler auslösen). Fehlernummer 105 ist im Vorbild intern
|
||
reserviert und sollte nicht via `ERROR n` verwendet werden.
|
||
- Fehler im aktiven Handler → sofortiger Abbruch. Fehler ohne Handler →
|
||
Abbruch mit Meldung „Fehlertext in Zeile n" bzw. Debugger in der IDE.
|
||
- Fehlerkatalog: klassische Codes 1–76, ISAM-Codes 80–89, Forms-Codes
|
||
260–480 — vollständig implementiert in `tb-runtime/src/errors.rs`.
|
||
|
||
## 8. Ereignis-Traps (klassisch, ohne Forms)
|
||
|
||
Vorhanden im Vorbild: `ON COM/KEY/PEN/PLAY/STRIG/TIMER/UEVENT(…) GOSUB label`
|
||
mit Steueranweisungen `… ON/OFF/STOP`, dazu das `EVENT`-Statement (auch für
|
||
Forms-Ereignisse) und `SetUEvent`. Timer-Events und Trapping sind ausgesetzt,
|
||
solange ein Menü-Control den Fokus hat. Für Terminal Basic relevant:
|
||
`TIMER`, `KEY`, `UEVENT`; `COM/PEN/PLAY/STRIG`: Nicht-Ziel (Fehler 73).
|
||
|
||
## 9. Konsolen-E/A
|
||
|
||
- `PRINT [#n,] liste` — Trennzeichen `;` (direkt anschließend) und `,`
|
||
(nächste 14-Zeichen-Druckzone). Zahlen: führendes Leerzeichen bzw. `-`,
|
||
nachgestelltes Leerzeichen. Abschluss ohne `;`/`,` → Zeilenumbruch.
|
||
- `PRINT USING "format"; liste` — Formatzeichen (offizielle Liste):
|
||
numerisch `#` Ziffer, `.` Dezimalpunkt, `,` Tausendertrennung, `+` Vorzeichen-
|
||
position, `-` nachgestelltes Minus, `$$` führendes $, `**` Füllsterne,
|
||
`**$` kombiniert, `^^^^` Exponent; String `&` ganz, `!` erstes Zeichen,
|
||
`\ \` n Zeichen (Leerzeichen + 2); `_` Literal-Escape; alle anderen
|
||
Zeichen literal.
|
||
- **Feldüberlauf** (geklärt 2026-09-04): Passt ein Wert nicht in sein
|
||
Feld, wird er vollständig ausgegeben und die Ausgabe mit `%`
|
||
eingeleitet (`PRINT USING "##"; 12345` → `%12345`). Die Original-Hilfe
|
||
listet das nicht; wir übernehmen das Verhalten der QB-Familie, weil ein
|
||
stilles Abschneiden Zahlen verfälschen würde.
|
||
- Vorzeichenangaben (`+` vorn, `+`/`-` hinten) belegen eine **eigene**
|
||
Position neben den Ziffernstellen; `$$` und `**` bringen ihre Stellen
|
||
dagegen mit.
|
||
- Die Formatzeichenkette wird wiederholt, solange Werte übrig sind. Eine
|
||
Zeichenkette ohne Feld bei vorhandenen Werten löst Fehler 5 aus.
|
||
- `SetFormatCC(ländercode)` setzt das Zeichen für `$$`. Nur wenige Codes
|
||
sind belegt (1 = `$`, 33 = `F`, 39 = `L`, 44 = `£`, 49 = `DM`,
|
||
81 = `¥`); alle übrigen behalten `$` — die Tabelle des Vorbilds ist
|
||
nicht überliefert (dokumentierte Abweichung).
|
||
- `INPUT ["prompt"{;|,}] var, …` (mit `;` vor Prompt: kein „? ");
|
||
`LINE INPUT` (ganze Zeile in String).
|
||
- `INKEY$` (nicht blockierend; "" wenn leer; erweiterte Tasten:
|
||
2-Zeichen-Sequenz `CHR$(0)+code` im Vorbild — Abweichung s. u.),
|
||
`INPUT$(n [,#f])`.
|
||
- `LOCATE [zeile][,spalte][,cursor an/aus][,start,ende]`, `CSRLIN`, `POS(0)`
|
||
— arbeiten auf der tatsächlichen Bildschirmgröße (dynamisch, s. Abweichungen).
|
||
- `COLOR [vg][,hg]` (vg 0–31: 16–31 = blinkend — bei uns als „hell"
|
||
simuliert, Entscheidung 2026-09-02; hg 0–7), `CLS`,
|
||
`WIDTH` (hebt den Puffer höchstens an, s. Abweichungen),
|
||
`VIEW PRINT oben TO unten` (Scrollbereich).
|
||
- `TAB(n)`, `SPC(n)` in PRINT-Listen.
|
||
- `BEEP`, `SOUND freq, dauer` (Terminal-Bell / Nicht-Ziel, s. Abweichungen),
|
||
`PLAY` (Nicht-Ziel).
|
||
- **Funktionstasten-Makros** (Umfang geklärt 2026-09-03 aus der
|
||
Original-Hilfe):
|
||
- `KEY n, text$` belegt ein Makro. `n` ist 1–10 für F1–F10 sowie 30 und
|
||
31 für F11/F12; andere Werte lösen Fehler 5 aus. `text$` wird nach
|
||
**15 Zeichen** abgeschnitten.
|
||
- `KEY LIST` gibt alle zwölf Makros untereinander aus.
|
||
- `KEY ON` blendet die Softkey-Zeile in der untersten Bildschirmzeile ein
|
||
(Tastennummer plus die ersten sechs Zeichen des Makros), `KEY OFF`
|
||
blendet sie aus. Solange sie sichtbar ist, endet der Scrollbereich
|
||
darüber, damit die Ausgabe sie nicht überschreibt; ein danach gesetztes
|
||
`VIEW PRINT` hat Vorrang.
|
||
- `KEY(n) ON|OFF|STOP` **mit** Index ist Ereignissteuerung und kommt mit
|
||
der Forms-Engine (Phase 4).
|
||
- Abweichung: ein kompiliertes Programm startet mit **leeren** Makros;
|
||
die Vorbelegung des Vorbilds gehörte zu dessen Interpreter-Oberfläche.
|
||
|
||
## 10. Datei-E/A
|
||
|
||
- `OPEN datei$ [FOR modus] [ACCESS zugriff] [lock] AS [#]n [LEN=reclen]`
|
||
Modi: `INPUT`, `OUTPUT`, `APPEND` (sequenziell), `RANDOM` (Standard),
|
||
`BINARY`.
|
||
- `CLOSE [#n, …]`, `RESET` (alle schließen).
|
||
- Sequenziell: `PRINT #`, `PRINT # USING`, `WRITE #` (CSV-artig, Strings in
|
||
`"…"`), `INPUT #`, `LINE INPUT #`, `EOF(n)`.
|
||
- Random: `FIELD #n, breite AS var$…` (klassisch) **und** `GET/PUT #n
|
||
[,satznr] [,var]` mit TYPE-Variablen; `LSET`/`RSET` für Feldpuffer.
|
||
- Binary: `GET/PUT #n, [pos], var`, `SEEK #n, pos` / `SEEK(n)`,
|
||
`LOC(n)`, `LOF(n)`.
|
||
- Verwaltung: `NAME alt$ AS neu$`, `KILL datei$`, `FILES [muster$]`,
|
||
`CHDIR`, `MKDIR`, `RMDIR`, `FILEATTR`, `FREEFILE`.
|
||
- Pfade: plattformneutral; `/` und `\` werden akzeptiert.
|
||
|
||
## 10a. ISAM (indizierter Satzzugriff)
|
||
|
||
ISAM der Professional-Edition des Vorbilds wird unterstützt (Entscheidung
|
||
2026-09-02). Das Dateiformat beschreibt
|
||
[dateiformate.md](dateiformate.md#isam-datenbank-open--for-isam).
|
||
|
||
- `OPEN datenbank$ FOR ISAM typname tabellenname$ AS [#]n` bindet eine
|
||
Tabelle an eine Dateinummer; die Felder des benannten `TYPE` sind ihre
|
||
Spalten. Die Tabelle entsteht beim ersten Öffnen. `CLOSE` löst die
|
||
Bindung; jede Operation außerhalb einer Transaktion ist für sich schon
|
||
festgeschrieben. Eine laufende Transaktion lässt `CLOSE` unberührt —
|
||
über ihr Ende entscheiden `COMMITTRANS` und `ROLLBACK ALL`; eine beim
|
||
Programmende offene Transaktion bleibt wirkungslos.
|
||
- Indizes: `CREATEINDEX [#]n, indexname$, eindeutig%, spalte$[, spalte$]…`,
|
||
`DELETEINDEX [#]n, indexname$`, `SETINDEX [#]n[, indexname$]`,
|
||
`GETINDEX$(n)`. `SETINDEX` ohne Namen wählt den NULL-Index
|
||
(Einfügereihenfolge); `GETINDEX$` liefert dafür einen leeren String.
|
||
- Sätze: `INSERT`, `RETRIEVE`, `UPDATE` (je `[#]n, satzvariable`) und
|
||
`DELETE [#]n` auf dem aktuellen Satz.
|
||
- Cursor: `MOVEFIRST`, `MOVELAST`, `MOVENEXT`, `MOVEPREVIOUS` (je `[#]n`)
|
||
in der Ordnung des aktiven Index; `EOF(n)` und `BOF(n)` melden das
|
||
Überlaufen am Ende bzw. am Anfang.
|
||
- Suche: `SEEKEQ`, `SEEKGT`, `SEEKGE` (je `[#]n, schlüsselwert[, …]`).
|
||
Ein mehrspaltiger Index lässt sich mit einem Präfix seiner Spalten
|
||
durchsuchen; `SEEKEQ` mit unvollständigem Schlüssel schlägt dabei immer
|
||
fehl (wie im Vorbild), `SEEKGE` und `SEEKGT` positionieren auf dem ersten
|
||
passenden Satz. Über den NULL-Index lässt sich nicht suchen (Fehler 87).
|
||
- Tabellen: `DELETETABLE datenbank$, tabellenname$`.
|
||
- Transaktionen: `BEGINTRANS`, `COMMITTRANS`, `SAVEPOINT` (Funktion,
|
||
liefert die Kennung), `ROLLBACK [kennung]` und `ROLLBACK ALL`.
|
||
- `LOF(n)` liefert für eine ISAM-Dateinummer die Satzzahl der Tabelle,
|
||
`LOC(n)` die Kennung des aktuellen Satzes (0 = unpositioniert).
|
||
- Cursorlage wie im Vorbild: nach `SETINDEX` ist der erste Satz der neuen
|
||
Ordnung der aktuelle; nach `DELETE` der auf den gelöschten folgende, und
|
||
war der gelöschte der letzte, steht der Cursor am Ende der Tabelle ohne
|
||
aktuellen Satz.
|
||
- Fehlercodes 81–89 (`ERR`): 81 ungültiger Name, 82 Tabelle nicht
|
||
gefunden, 83 Index nicht gefunden, 84 ungültige Spalte, 85 kein aktueller
|
||
Satz, 86 Verstoß gegen einen eindeutigen Index, 87 unzulässige Operation
|
||
auf dem NULL-Index (Suche), 88 Datenbank inkonsistent, 89 ISAM-Puffer zu
|
||
klein.
|
||
|
||
**Sortierordnung der Indizes.** Textspalten ordnen nach
|
||
**Unicode-Codepoints**; Groß- und Kleinschreibung werden **unterschieden**.
|
||
Es ist dieselbe Ordnung, die der Vergleichsoperator des Dialekts auf
|
||
Strings anwendet — ein Programm kann die Satzreihenfolge damit
|
||
vorhersagen. Praktisch heißt das: alle Großbuchstaben `A`–`Z` stehen vor
|
||
allen Kleinbuchstaben `a`–`z`, und Zeichen jenseits von ASCII (`Ä`, `ß`,
|
||
`€`) stehen nach beiden, in der Reihenfolge ihrer Codepoints. Beispiel:
|
||
`Apfel` < `Birne` < `apfel` < `birne` < `Äpfel`.
|
||
|
||
Eine sprachabhängige (locale-abhängige) Sortierung gibt es bewusst nicht:
|
||
sie machte die Reihenfolge plattformabhängig. Das Vorbild ordnete nach den
|
||
Zeichencodes einer festen Codepage; Codepoints sind die direkte
|
||
Entsprechung unter der Unicode-Entscheidung dieses Projekts.
|
||
|
||
**Absteigende Indexspalten** (Erweiterung). Die Original-Hilfe kennt bei
|
||
`CREATEINDEX` keine Sortierrichtung. Terminal Basic ordnet eine Spalte
|
||
absteigend, wenn ihrem Namen ein `-` vorangestellt ist:
|
||
|
||
```basic
|
||
CREATEINDEX #1, "NachPreis", 0, "Nachname", "-Preis"
|
||
```
|
||
|
||
Das ist kollisionsfrei, weil ein Feldname einer `TYPE`-Anweisung nie mit
|
||
`-` beginnen kann; Programme des Vorbilds bleiben unverändert gültig.
|
||
|
||
**`SETMEM` und Fehler 89** (Abweichung). Im Vorbild veränderte
|
||
`SETMEM(n&)` den DOS-Fern-Heap um `n` Bytes. Ein DOS-Speichermodell gibt
|
||
es hier nicht; stattdessen ist `SETMEM` die Obergrenze des ISAM-Puffers:
|
||
sie wird um `n` Bytes verändert (negativ verkleinert, positiv vergrößert)
|
||
und die danach verfügbare Größe geliefert. Der Vorgabewert ist **65536
|
||
Bytes**. Gegen die Grenze zählen die Satzbytes einer laufenden Operation
|
||
und das Rücknahmeprotokoll einer offenen Transaktion. Reicht der Puffer
|
||
für eine Operation nicht aus, tritt Laufzeitfehler 89 („ISAM -
|
||
Insufficient ISAM buffers") auf — nicht ein generischer Fehler und kein
|
||
stiller Abbruch.
|
||
|
||
|
||
## 11. Eingebaute Funktionen (Katalog)
|
||
|
||
**Strings:** `LEN`, `LEFT$`, `RIGHT$`, `MID$` (Funktion **und** Anweisung),
|
||
`INSTR([start,] s$, such$)`, `UCASE$`, `LCASE$`, `LTRIM$`, `RTRIM$`,
|
||
`SPACE$`, `STRING$(n, zeichen|code)`, `ASC`, `CHR$`, `STR$`, `VAL`,
|
||
`HEX$`, `OCT$`, `LSET`/`RSET` (Anweisungen).
|
||
|
||
**Mathematik:** `ABS`, `SGN`, `INT` (abrunden), `FIX` (Richtung 0), `SQR`,
|
||
`EXP`, `LOG`, `SIN`, `COS`, `TAN`, `ATN`, `RND[(n)]`, `RANDOMIZE [saat]`,
|
||
`CINT`, `CLNG`, `CSNG`, `CDBL`, `CCUR`.
|
||
|
||
**Zufallszahlen.** Der Generator ist der lineare Kongruenzgenerator des
|
||
Vorbilds auf 24 Bit:
|
||
`zustand = (zustand · &H FD43FD + &H C39EC3) MOD 2^24`, Ergebnis
|
||
`zustand / 2^24` als SINGLE im Bereich [0, 1).
|
||
`RND` und `RND(n)` mit `n > 0` liefern die nächste Zahl, `RND(0)`
|
||
wiederholt die zuletzt gelieferte, `RND(n)` mit `n < 0` setzt den Zustand
|
||
aus dem Argument. `RANDOMIZE saat` bildet aus der Saat 16 Bit, die die
|
||
Bits 8–23 des Zustands besetzen; das niederwertige Byte ist fest — damit
|
||
liefert dieselbe Saat stets dieselbe Folge, unabhängig von vorherigen
|
||
`RND`-Aufrufen. `RANDOMIZE` ohne Argument fragt den Startwert mit
|
||
`Random Number Seed (-32768 to 32767)?` ab.
|
||
|
||
**Datum/Zeit:** `DATE$` (Funktion und Anweisung — Setzen: Nicht-Ziel),
|
||
`TIME$`, `TIMER` (Sekunden seit Mitternacht, SINGLE); dazu die nativen
|
||
Schlüsselwörter des Vorbilds (Neuerung ggü. QB/PDS): `NOW`, `DATESERIAL`,
|
||
`DATEVALUE`, `TIMESERIAL`, `TIMEVALUE`, `DAY`, `MONTH`, `YEAR`, `WEEKDAY`,
|
||
`HOUR`, `MINUTE`, `SECOND` (serielle Datumswerte als DOUBLE) sowie
|
||
`FORMAT$` (Zahlen-, Datums-/Zeitformatierung).
|
||
|
||
**Sonstiges:** `LBOUND`, `UBOUND`, `FRE(…)` (freier Speicher — liefert bei
|
||
uns Pseudowerte), `VARPTR`/`VARSEG`/`SADD`/`PEEK`/`POKE` → Nicht-Ziel
|
||
(Fehler 73), `ENVIRON$`, `COMMAND$`, `SHELL [cmd$]`.
|
||
|
||
**DATA:** `DATA konstanten`, `READ var, …`, `RESTORE [label]`.
|
||
|
||
## 12. Forms-Anbindung (Details in forms-referenz.md)
|
||
|
||
`form.eigenschaft = wert`, `form!steuerelement.eigenschaft`,
|
||
Ereignisprozeduren `SUB name_Ereignis (…)`, `LOAD`/`UNLOAD form`,
|
||
`form.SHOW [style%]` (0 = modeless, Standard; 1 = modal), `form.HIDE`,
|
||
`DOEVENTS()`, `SCREEN`-Objekt, Datentypen `FORM`/`CONTROL`, `IF TYPEOF`,
|
||
`$FORM`-Metabefehl, Include-Dateien (Konstanten-`.BI`).
|
||
|
||
Dialoge:
|
||
- `MSGBOX msg$ [,type% [,title$]]` (Anweisung) bzw. Funktion → INTEGER.
|
||
`type%` = Buttons (0 OK · 1 OK/Cancel · 2 Abort/Retry/Ignore ·
|
||
3 Yes/No/Cancel · 4 Yes/No · 5 Retry/Cancel) + Default-Button
|
||
(0/256/512). Rückgabe 1 OK · 2 Cancel/Esc · 3 Abort · 4 Retry ·
|
||
5 Ignore · 6 Yes · 7 No. Keine Icon-Flags (Textmodus). msg$ max. 1024
|
||
Zeichen, modal.
|
||
- `INPUTBOX$(prompt$ [,title$ [,default$ [,xpos%, ypos%]]])` → STRING;
|
||
Cancel liefert `""`; Position in **Zeichen**, sonst zentriert; Dialog
|
||
fest 46×16 Zeichen; prompt$ max. 255 Zeichen.
|
||
|
||
---
|
||
|
||
## Abweichungen vom Vorbild (beschlossen)
|
||
|
||
1. **UTF-8/Unicode statt CP437** (2026-09-01). Konsequenzen:
|
||
- STRING ist eine Folge von Unicode-Zeichen (Codepoints); `LEN` zählt
|
||
Zeichen, nicht Bytes. `ASC`/`CHR$` arbeiten auf Codepoints
|
||
(`CHR$(9731)` = „☃"). `ASC("")` bleibt Fehler 5.
|
||
- String-Vergleich codepoint-weise (keine CP437-Sortierung).
|
||
- Der Bildschirmpuffer speichert Unicode-Zeichen; Rahmen werden mit
|
||
Unicode-Box-Drawing gezeichnet.
|
||
- Zeichen mit Darstellungsbreite 2 (Emoji, CJK) **belegen zwei Zellen**
|
||
(Entscheidung 2026-09-02, umgesetzt): der Cursor rückt um zwei Spalten
|
||
vor, `POS` nennt die Anfangsspalte, `LOCATE` auf die zweite Hälfte
|
||
wirkt auf den Zeichenanfang. Passt ein breites Zeichen nicht mehr in
|
||
die Zeile, wandert es als Ganzes in die nächste und die letzte Spalte
|
||
bleibt leer. Wird eine Hälfte überschrieben, verschwindet die andere.
|
||
Zeichen der Breite 0 (kombinierende Zeichen) belegen vorerst eine
|
||
eigene Zelle.
|
||
- `GET`/`PUT`-Records: feste Strings (`STRING * n`) werden als **UTF-32**
|
||
gespeichert (4 Bytes/Zeichen, feste Record-Länge) — Binärdateien sind
|
||
dadurch bewusst inkompatibel zum Vorbild (Entscheidung 2026-09-02).
|
||
2. **Keine Hardware-Nähe** (bestätigt 2026-09-02): `PEEK`/`POKE`/`INP`/
|
||
`OUT`/`WAIT`/`BLOAD`/`BSAVE`/`VARPTR`/`VARSEG`/`SADD`/`CALL ABSOLUTE`/
|
||
`CALL INTERRUPT`/`IOCTL` werden bereits **zur Compile-Zeit** mit der
|
||
Meldung „Feature unavailable" abgewiesen (kein Laufzeitfehler).
|
||
Ebenso abgewiesen: die Interlanguage-Schnittstelle (`CALLS`, `SSEG`,
|
||
`SSEGADD`, `StringAddress`/`StringAssign`/`StringLength`/
|
||
`StringRelease`), die Eingabegeräte `PEN`, `STRIG`, `STICK` und die
|
||
serielle Schnittstelle (`COM`, `ON COM`, `OPEN "COMn:"`).
|
||
3. **`INKEY$` für erweiterte Tasten** liefert `CHR$(0) + code`-Sequenzen
|
||
mit den klassischen Scancodes, damit bestehender Code funktioniert
|
||
(festgelegt 2026-09-04): F1–F10 = 59–68, Pos1 = 71, ↑ = 72, Bild↑ = 73,
|
||
← = 75, → = 77, Ende = 79, ↓ = 80, Bild↓ = 81, Einfg = 82, Entf = 83.
|
||
Zeichentasten liefern ein Zeichen, Eingabe `CHR$(13)`, Rückschritt
|
||
`CHR$(8)`, Esc `CHR$(27)`, Tabulator `CHR$(9)`. Strg+C und Strg+Untbr
|
||
sind Abbruch und erscheinen nicht in `INKEY$`. Tasten heutiger
|
||
Tastaturen ohne Entsprechung im Vorbild (etwa F11/F12 oder Meta-
|
||
Kombinationen) liefern nichts — eine eigene Kodierung dafür wäre eine
|
||
Erweiterung und gehört in Stufe 2.
|
||
4. **Kein `CHAIN`/Overlay-Mechanismus**; `SHELL` startet die System-Shell.
|
||
5. **Grafik-Anweisungen** (`SCREEN n>0`, `PSET`, `PRESET`, `LINE`,
|
||
`CIRCLE`, `PAINT`, `DRAW`, Grafik-`GET`/`PUT`, `PALETTE`, `WINDOW`,
|
||
`VIEW`, `PMAP`, `PCOPY`, `POINT`) sind Nicht-Ziel und werden **zur
|
||
Compile-Zeit** mit „Feature unavailable" abgewiesen. Die Formen, die
|
||
sich ein Schlüsselwort mit einer unterstützten Anweisung teilen, erkennt
|
||
der Compiler an ihrer Syntax: `LINE` ohne `INPUT`, `GET`/`PUT` mit
|
||
`(`, `VIEW` ohne `PRINT`. Hinweis: das Vorbild **hatte** die
|
||
Grafikmodi 0–13, aber nicht gleichzeitig mit Forms (dort Fehler 272
|
||
„Invalid when forms are showing") — wir übernehmen nur den
|
||
Forms-/Textmodus-Zweig.
|
||
6. **`PLAY`/`SOUND`**: `BEEP` = Terminal-Bell; Rest Nicht-Ziel (Fehler 73).
|
||
5a. **`LPRINT` schreibt in eine Datei** (2026-09-03): einen
|
||
plattformübergreifenden Druckerkanal gibt es nicht. Der Druckerpuffer
|
||
geht am Programmende in `LPT1.TXT` im aktuellen Verzeichnis; `LPOS`
|
||
nennt die Spalte in diesem Puffer.
|
||
5b. **Satzsperren** (`LOCK`/`UNLOCK`) prüfen nur, ob die Datei offen ist;
|
||
eine echte Sperre bietet die Plattform nicht einheitlich. `FILEATTR`
|
||
liefert nur die Modus-Kennzahl (Argument 1); das
|
||
Betriebssystem-Handle bildet der Dialekt nicht ab und liefert 0.
|
||
5c. **`CHDRIVE`** ist außerhalb von Windows folgenlos — Laufwerksbuchstaben
|
||
gibt es dort nicht.
|
||
6a. **Uhr in Ortszeit, mit Rückfall auf UTC** (2026-09-04): `DATE$`,
|
||
`TIME$`, `TIMER` und `NOW` rechnen in der Zeitzone des Rechners;
|
||
`TIMER` zählt ab der **lokalen** Mitternacht. Der Versatz wird für den
|
||
jeweils abgefragten Zeitpunkt bestimmt, nicht einmal beim Start — ein
|
||
Programm, das über eine Zeitumstellung hinweg läuft, folgt ihr.
|
||
Lässt sich die Zone des Rechners nicht ermitteln, gilt UTC; das
|
||
Programm läuft weiter, und der Runner meldet es einmal beim Start auf
|
||
der Fehlerausgabe („Zeitzone nicht ermittelbar …"). `DATE$ = …` und
|
||
`TIME$ = …` stellen nicht die Systemuhr, sondern einen programmeigenen
|
||
Versatz auf die Ortszeit; alle Zeitfunktionen rechnen ihn mit ein.
|
||
6b. **Serielle Datumswerte** zählen Tage ab dem 30.12.1899 (Serie 2 =
|
||
1.1.1900), der Nachkommaanteil ist die Tageszeit. `WEEKDAY` liefert 1
|
||
für Sonntag. Zweistellige Jahre gelten in `DATESERIAL` als 19xx, in
|
||
`DATEVALUE` als 20xx unter 80 und sonst als 19xx.
|
||
7. **Dynamische Bildschirmgröße** (2026-09-02): kein festes 80×25 — der
|
||
Bildschirm folgt der Terminalgröße (Mindestgröße 80×25, darunter nur
|
||
ein Hinweis). `CSRLIN`/`POS`/`LOCATE`/`VIEW PRINT` und das
|
||
`SCREEN`-Objekt arbeiten auf der tatsächlichen Größe. `WIDTH 40/80`
|
||
wird akzeptiert, ändert aber nichts (die Größe bestimmt das Terminal).
|
||
80×25 ist überall nur die **untere Schranke der Darstellung**, nie eine
|
||
Bildschirmgröße im Verhalten des Programms. Ändert sich die Größe
|
||
**während der Ausführung**, wird das zugestellt: der Inhalt bleibt oben
|
||
links erhalten, der Cursor wird in die neuen Grenzen geklemmt, und ein
|
||
`VIEW PRINT`-Bereich wird am unteren Rand geklemmt bzw. — wenn er ganz
|
||
vom Schirm fällt — auf den vollen Bildschirm zurückgesetzt.
|