====== Lyx – Datentypen ====== Stand: v0.9.0 Das Typsystem von Lyx ist **explizit und statisch**: Jede Variable hat einen festen Typ, der zur Compile-Zeit bekannt ist. Implizite Konvertierungen gibt es nicht – alle Typumwandlungen müssen mit dem ''as''-Operator explizit geschrieben werden. Dadurch werden typische Fehlerquellen (truncation, sign-confusion, float-to-int-Überraschungen) bereits beim Compilieren erkannt. ===== 1. Ganzzahl-Typen (signiert) ===== Alle signierten Ganzzahl-Typen verwenden Zweierkomplement-Darstellung. Integer-Arithmetik **wraps** bei Überlauf (modular arithmetic); für explizite Überlauf-Erkennung stehen die Safe-Varianten aus ''std.result'' zur Verfügung. ^ Typ ^ Bits ^ Wertebereich ^ Literal ^ Typisches Einsatzgebiet ^ | ''int8'' | 8 | −128 … 127 | ''42i8'' | Kompakte Felder, Protokoll-Bytes mit Vorzeichen | | ''int16'' | 16 | −32 768 … 32 767 | ''42i16'' | Audio-Samples, kompakte Sensordaten | | ''int32'' | 32 | −2 147 483 648 … 2 147 483 647 | ''42i32'' | Koordinaten, API-Rückgabewerte | | ''int64'' | 64 | −9 223 372 036 854 775 808 … 9 223 372 036 854 775 807 | ''42'' | Standard-Ganzzahl in Lyx | | ''int'' | 64 | Alias für ''int64'' | ''42'' | Kurzform, identisch mit int64 | var a: int64 := 9_000_000_000; // Unterstriche als Tausender-Trenner var b: int8 := -100i8; var c: int32 := 2_147_483_647i32; // Überlauf-sicheres Addieren (std.result) import std.result; var result := SafeAdd(a, 1); if (ResultInt64IsOk(result)) { PrintInt(Unwrap(result)); } else { PrintStr("Überlauf erkannt"); } ===== 2. Ganzzahl-Typen (unsigniert) ===== Unsignierte Typen sind ideal für Bit-Operationen, Hardware-Register (MMIO), Speichergrößen und Protokoll-Felder, die keine negativen Werte kennen. ^ Typ ^ Bits ^ Wertebereich ^ Literal ^ Typisches Einsatzgebiet ^ | ''uint8'' | 8 | 0 … 255 | ''42u8'' | Bytes, MMIO-Register, Protokoll-Rohdaten | | ''uint16'' | 16 | 0 … 65 535 | ''42u16'' | Ports, Checksummen, kompakte Flags | | ''uint32'' | 32 | 0 … 4 294 967 295 | ''42u32'' | IPv4-Adressen, CRC32, Hardware-Register | | ''uint64'' | 64 | 0 … 18 446 744 073 709 551 615 | ''42u64'' | Datei-Offsets, Hash-Werte, 64-Bit-Masken | // Bit-Manipulation (typischer uint32-Einsatz) var flags: uint32 := 0x00u32; con FLAG_READ: uint32 := 0x01u32; con FLAG_WRITE: uint32 := 0x02u32; con FLAG_EXEC: uint32 := 0x04u32; flags := flags | FLAG_READ | FLAG_WRITE; // Bits setzen var canRead := flags & FLAG_READ != 0u32; // Bit testen flags := flags & ~FLAG_WRITE; // Bit löschen // Hex-Literale für MMIO var GPIO_BASE: ^uint32 := 0x3FF44000 as ^uint32; ===== 3. Plattform-abhängige Typen ===== ^ Typ ^ Beschreibung ^ Typisches Einsatzgebiet ^ | ''isize'' | Pointer-Größe, signiert (64-Bit auf allen unterstützten Plattformen) | Pointer-Differenz, Offsets | | ''usize'' | Pointer-Größe, unsigniert | Array-Indizes, Speichergrößen | ''usize'' ist der empfohlene Typ für Array-Indizes und Längenangaben, da er auf der Zielplattform immer groß genug für jeden Speicherzeiger ist. var len: usize := 1024; for i := 0 to (len as int64) - 1 do { // ... } ===== 4. Fließkomma-Typen ===== Beide Typen folgen **IEEE 754**. Fließkomma-Literale ohne Suffix haben den Typ ''f64''. ^ Typ ^ Bits ^ Präzision ^ Wertebereich (ca.) ^ Literal ^ | ''f32'' | 32 | ~7 Dezimalstellen | ±3.4 × 10³⁸ | ''3.14f32'' | | ''f64'' | 64 | ~15 Dezimalstellen | ±1.8 × 10³⁰⁸ | ''3.14'' | var pi: f64 := 3.14159265358979; var small: f32 := 1.5f32; var zero: f64 := 0.0; // Expliziter Cast nötig bei gemischten Typen var mixed: f64 := (small as f64) + pi; // Wissenschaftliche Notation var nano: f64 := 1.0e-9; var giga: f64 := 1.0e9; ==== Besondere IEEE-754-Werte ==== ^ Wert ^ Entsteht durch ^ Verhalten ^ | ''NaN'' (Not a Number) | ''0.0 / 0.0'', ''Sqrt(-1.0)'' | Jeder Vergleich mit NaN ergibt ''false'' | | ''+Inf'' | ''1.0 / 0.0'' | Größer als jede endliche Zahl | | ''-Inf'' | ''-1.0 / 0.0'' | Kleiner als jede endliche Zahl | > **Safety-Hinweis:** > In ''@flight_crit''-Modulen aktiviert der Compiler FPU-Traps für NaN und Inf. Jede entstehende ''NaN''/''Inf''-Operation löst einen ''panic'' aus, statt still einen ungültigen Wert weiterzupropagieren. ===== 5. Wahrheitswerte – bool ===== ''bool'' nimmt genau die Werte ''true'' und ''false'' an. Im Speicher belegt er 1 Byte (Wert 0 oder 1). In der ABI wird er wie ein Integer-Register behandelt. var ready: bool := false; var done: bool := true; // Logische Operatoren var both := ready & done; // AND var any := ready | done; // OR var inv := !ready; // NOT // Vergleiche ergeben bool var big := (42 > 10); // true var eq := (3 = 3); // true (= ist Vergleich, nicht Zuweisung!) ===== 6. Probabilistischer Typ – qbool ===== ''qbool'' ist Lyxs probabilistischer Wahrheitswert: ein ''f64''-Wert im Bereich 0.0 (sicher falsch) bis 1.0 (sicher wahr). Er ermöglicht unscharfe Logik in KI-Anwendungen und IoT-Sensorfusion. ^ Literal ^ Bedeutung ^ | ''0.0q'' | Definitiv falsch | | ''0.5q'' | Unbekannt / gleichwahrscheinlich | | ''1.0q'' | Definitiv wahr | | ''0.75q'' | Wahrscheinlich wahr (75 %) | import std.qbool; var smoke: qbool := 0.8q; // Rauchsensor: 80 % sicher var heat: qbool := 0.9q; // Temperatursensor: 90 % sicher var alarm := QBoolAnd(smoke, heat); // 0.72q (kombiniert) var alert := Observe(alarm); // bool: true (über Schwellwert) // Operationen var neg := QBoolNot(smoke); // 0.2q var either := QBoolOr(smoke, heat); // 0.98q var xor := QBoolXor(smoke, heat); // |0.8 - 0.9| = 0.1q In der ABI wird ''qbool'' als ''f64'' behandelt (Float-Register XMM0/V0/fa0). → Details: [[lyx_-_programmiersprache:das-energy-aware-programmiermodell|Energy-Aware Programmiermodell – QBool]] ===== 7. Zeichen und Strings ===== ==== char – Ein Zeichen ==== ''char'' speichert ein einzelnes ASCII-Zeichen (1 Byte, unsigniert). var c: char := 'A'; var nl: char := '\n'; var tab: char := '\t'; // ASCII-Wert als Zahl var code: int64 := c as int64; // 65 ==== pchar – Nullterminierter String ==== ''pchar'' ist ein Alias für ''%%^%%uint8'' (C-kompatibles ''char*''). String-Literale haben immer den Typ ''pchar'' und enden implizit mit ''\0''. var greeting: pchar := "Hallo Lyx\n"; var empty: pchar := ""; let version: pchar := "v0.9.0"; // An Funktionen übergeben PrintStr(greeting); // pchar ist ein Pointer – kein eingebautes bounds-checking // Sicherer String-Zugriff: std.string nutzen import std.string; var len := StrLen(greeting); // 10 ^ Situation ^ Empfehlung ^ | Ausgabe, FFI-Grenze | ''pchar''-Literal direkt übergeben | | Zeichen lesen / suchen | ''std.string.CharAt'', ''std.string.StrFind'' | | String manipulieren | ''std.string.StringBuilder'' | | Ownership-Übergabe | Kommentar: wer ist für ''free'' verantwortlich | ===== 8. Pointer-Typen ===== Pointer speichern Speicheradressen. Alle Pointer-Typen sind intern 8-Byte-Integer (''int64''). ^ Notation ^ Bedeutung ^ | ''%%^T%%'' | Pointer-Typ auf T | | ''%%^x%%'' | Adresse von x (Address-of-Operator) | | ''%%p^%%'' | Wert an Adresse p (Dereferenz) | | ''nil'' | Null-Pointer (kein gültiges Ziel) | | ''T?'' | Nullable Typ (darf nil sein) | var x: int64 := 42; var p: ^int64 := ^x; // p zeigt auf x p^ := 99; // x ist jetzt 99 // Nullable var opt: pchar? := GetOptionalName(); var name: pchar := opt ?? "Gast"; // Fallback wenn nil → Details: [[lyx_-_programmiersprache:pointer-inlining|Pointer & Inlining – vollständige Referenz]] ===== 9. Array-Typen ===== Lyx unterscheidet zwei Array-Formen mit unterschiedlichem Speicherort und Größenverhalten. ==== [N]T – Festes Array (Stack) ==== Größe muss eine Compile-Zeit-Konstante sein. Das Array lebt auf dem Stack oder im Data-Segment. Kein Heap-Zugriff. var temps: [7]f64; // 7 Elemente, auf dem Stack var buf: [1024]uint8; // 1 KiB Byte-Buffer con SIZE := 64; var data: [SIZE]int64; // Konstante als Größe erlaubt // Zugriff temps[0] := 20.5; temps[6] := 19.8; // Länge über len() for i := 0 to len(temps) - 1 do { PrintFloat(temps[i]); } Feste Arrays sind **Fat Pointer**: Neben dem Daten-Pointer speichert Lyx intern Länge (len) und Kapazität (cap) mit. Das ergibt 24 Byte pro Fat-Pointer-Deskriptor. ==== array – Dynamisches Array (Heap) ==== Größe wächst zur Laufzeit. Lebt auf dem Heap; muss explizit freigegeben werden. var list: array := [1, 2, 3, 4, 5]; list.push(6); list.push(7); PrintInt(list.len()); // 7 PrintInt(list[0]); // 1 > **In ''@flight_crit''- und ''@dal(A)''-Modulen ist ''array'' verboten** (Heap-Allokation). Dort ausschließlich ''[N]T''-Arrays auf dem Stack verwenden. ==== parallel Array – SIMD-optimiert ==== Für SIMD-Verarbeitung (AVX2, NEON) existiert eine spezielle Array-Form mit garantierter Ausrichtung: var signal: parallel Array(1024); // SIMD-ausgerichtetes Array @parallel for i := 0 to 1023 do { signal[i] := signal[i] * 2.0f32; // 8 Elemente pro AVX2-Instruktion } ^ Typ ^ Speicherort ^ Größe ^ Bounds-Check ^ Safety-Code ^ | ''[N]T'' | Stack / Data | Compile-Zeit-Konstant | ✅ Ja | ✅ Erlaubt | | ''array'' | Heap | Dynamisch | ✅ Ja | ❌ Verboten | | ''parallel Array'' | Heap (SIMD-aligned) | Dynamisch | ✅ Ja | ❌ Verboten | ===== 10. Map – Assoziatives Array ===== ''Map'' ist eine Hash-Map mit typisiertem Schlüssel und Wert. Literale werden mit ''{key: value}''-Syntax geschrieben. var scores: Map := { "Alice": 95, "Bob": 87, "Carol": 92 }; scores["Dave"] := 78; // Einfügen / Überschreiben var val := scores["Alice"]; // 95 var has := scores.Has("Eve"); // false scores.Delete("Bob"); PrintInt(scores.Len()); // 3 ===== 11. Verbundtypen ===== ==== struct – Werttyp (Stack) ==== Structs sind stack-allozierte Werttypen. Zuweisung kopiert den gesamten Inhalt. type Point = struct { x: f64; y: f64; fn Distance(other: Point): f64 { var dx := self.x - other.x; var dy := self.y - other.y; return Sqrt(dx * dx + dy * dy); } }; var p1 := Point { x: 0.0, y: 0.0 }; var p2 := Point { x: 3.0, y: 4.0 }; PrintFloat(p1.Distance(p2)); // 5.0 var p3 := p1; // Kopie – p3 und p1 sind unabhängig ==== class – Referenztyp (Heap) ==== Klassen werden mit ''new'' auf dem Heap alloziert. Zuweisung kopiert den **Pointer**, nicht den Inhalt. type Counter = class { value: int64; fn Create(start: int64) { self.value := start; } fn Increment() { self.value++; } fn Get(): int64 { return self.value; } }; var c := new Counter(0); c.Increment(); c.Increment(); PrintInt(c.Get()); // 2 dispose c; ==== enum – Aufzählungstyp ==== Enums definieren benannte Ganzzahl-Konstanten. Der Compiler prüft Exhaustivität in ''match''-Ausdrücken. enum Direction { North, South, East, West } enum HttpStatus { Ok = 200, NotFound = 404, ServerErr = 500 } var dir := Direction::North; var code := HttpStatus::Ok; match (dir) { case Direction::North => PrintStr("N"); case Direction::South => PrintStr("S"); case Direction::East => PrintStr("E"); case Direction::West => PrintStr("W"); } ^ Merkmal ^ struct ^ class ^ enum ^ | Speicherort | Stack | Heap | Code (Immediate) | | Zuweisung | Kopiert Wert | Kopiert Pointer | Kopiert Wert | | Freigabe | Automatisch (Scope-Ende) | ''dispose'' | Automatisch | | Methoden | ✅ | ✅ | ❌ | | Vererbung | ❌ | ✅ (''extends'') | ❌ | ===== 12. Range-Typen (Sicherheitskritisch) ===== Lyx erlaubt die Definition von Integer-Typen mit eingeschränktem Wertebereich. Sie sind ein Kernmerkmal für DO-178C-konforme Software. type Altitude = int64 range -1000..60000; // Meter ü. NN type Speed = int64 range 0..300; // km/h type DalLevel = int8 range 1..5; type Temp = int32 range -273..1000; // Celsius ==== Compile-Zeit-Prüfung ==== Literale außerhalb des Bereichs erzeugen einen Fehler: var alt: Altitude := 70000; // ✗ Compiler-Fehler: 70000 liegt außerhalb -1000..60000 var spd: Speed := -10; // ✗ Compiler-Fehler: -10 liegt außerhalb 0..300 ==== Laufzeit-Prüfung ==== Nicht-konstante Zuweisungen werden durch IR-Checks überwacht: var raw := ReadSensor(); // Laufzeit-Wert, unbekannt var alt: Altitude := raw; // Compiler fügt Bereichsprüfung ein: // if raw < -1000 or raw > 60000 → panic ==== Range-Typen in Safety-Code ==== @dal(B) @flight_crit unit flightcontrol; type Altitude = int64 range -1000..60000; type BankAngle = int32 range -60..60; // Grad fn SetAltitude(target: Altitude) { // Wertebereich ist zur Compile-Zeit garantiert – kein zusätzlicher nil-Check nötig ActuatorSetAltitude(target); } > **DO-178C-Hinweis:** > Range-Typen erfüllen die Anforderung an **Datensicherheit** (Data Coupling, DO-178C Abschnitt 6.3.2). Sie verhindern ungültige Systemzustände durch strikte Prüfung zur Compile- und Laufzeit. Der ''--lint''-Pass meldet alle Stellen, an denen ein Range-Typ mit einem ungeprüften Wert belegt wird. ===== 13. Typ-Inferenz ===== Wenn der Typ aus dem Initialisierer eindeutig bestimmbar ist, kann die Typ-Annotation weggelassen werden: var x := 42; // int64 (Standard-Integer) var f := 3.14; // f64 (Standard-Float) var b := true; // bool var s := "Hallo"; // pchar var p := 0.75q; // qbool var i := 42i32; // int32 (durch Suffix) var u := 0xFFu8; // uint8 (durch Hex + Suffix) Explizite Annotation ist empfohlen, wenn: * Der Typ nicht auf den ersten Blick klar ist * Ein präziserer Typ gewünscht ist (''int32'' statt ''int64'') * Range-Typen oder ''con''-Konstanten deklariert werden ===== 14. Typ-Konvertierung (as) ===== Alle Typkonvertierungen sind **explizit**. Der ''as''-Operator führt die Konvertierung durch. var i: int64 := 1000; var b: uint8 := i as uint8; // Truncation: 1000 mod 256 = 232 var f: f64 := i as f64; // 1000.0 var r: int32 := i as int32; // 1000 (passt in int32) // Pointer-Casts var addr: int64 := 0x40020000; var reg: ^uint32 := addr as ^uint32; // Integer → Pointer var back: int64 := reg as int64; // Pointer → Integer ==== Konvertierungs-Regeln ==== ^ Von → Nach ^ Verhalten ^ | ''intN'' → breiterer int | Vorzeichen-Erweiterung (sign-extend) | | ''intN'' → schmälerer int | Abschneiden (truncation, modular) | | ''intN'' → ''uintN'' | Bit-Reinterpretation (kein Wert-Check) | | ''intN'' → ''f64'' | Exakte Konvertierung (bis ±2⁵³) | | ''f64'' → ''intN'' | Abschneiden Richtung Null (floor toward zero) | | ''f32'' → ''f64'' | Präzisions-Erweiterung (verlustfrei) | | ''f64'' → ''f32'' | Präzisions-Verlust (rounding to nearest) | | Pointer → ''int64'' | Adress-Wert als Integer | | ''int64'' → Pointer | Integer als Adresse (nur in ''unsafe'' oder MMIO) | > **Linter-Warnung bei riskantem Cast:** > ''--lint'' warnt bei Konvertierungen, die Datenverlust verursachen können (f64 → int32, int64 → uint8 ohne vorherige Prüfung). In ''@dal(A)''-Modulen sind solche Casts ohne expliziten Kommentar ein Compiler-Fehler. ===== 15. Typ-Prüfung (is) ===== Der ''is''-Operator prüft den Laufzeit-Typ eines Objekts (nur für Klassen mit Vererbung): type Shape = class { virtual fn Area(): f64; }; type Circle = class extends Shape { radius: f64; ... }; type Rect = class extends Shape { w: f64; h: f64; ... }; fn Describe(s: Shape) { if (s is Circle) { var c := s as Circle; // Sicherer Cast nach is-Prüfung PrintFloat(c.radius); } else if (s is Rect) { var r := s as Rect; PrintFloat(r.w); } } ===== 16. Speicherklassen (Kurzübersicht) ===== Die Speicherklasse bestimmt Mutabilität und Lebenszeit einer Variable: ^ Keyword ^ Änderbar ^ Beschreibung ^ | ''var'' | ✅ | Standard-Variable; kann jederzeit neu zugewiesen werden | | ''let'' | ❌ (nach Init) | Einmalige Laufzeit-Zuweisung; danach read-only | | ''co'' | ❌ | Read-only Stack-Slot | | ''con'' | ❌ | Compile-Zeit-Konstante; wird direkt eingebettet (Immediate) | → Details: [[lyx_-_programmiersprache:syntax|Syntax-Referenz – Speicherklassen]] ===== 17. Vollständige Typ-Übersicht ===== ^ Kategorie ^ Typen ^ Speicherort ^ ABI-Register ^ | Ganzzahl (signiert) | ''int8'', ''int16'', ''int32'', ''int64'' / ''int'' | Stack / Register | GPR | | Ganzzahl (unsigniert) | ''uint8'', ''uint16'', ''uint32'', ''uint64'' | Stack / Register | GPR | | Plattform | ''isize'', ''usize'' | Stack / Register | GPR | | Fließkomma | ''f32'', ''f64'' | Stack / Register | FPU | | Wahrheitswert | ''bool'' | Stack / Register | GPR (0/1) | | Probabilistisch | ''qbool'' | Stack / Register | FPU (als f64) | | Zeichen | ''char'' | Stack / Register | GPR | | String | ''pchar'' | Stack (Pointer) | GPR | | Pointer | ''%%^T%%'', ''T?'' | Stack (8 Byte) | GPR | | Festes Array | ''[N]T'' | Stack | – (Fat Pointer) | | Dynamisches Array | ''array'' | Heap | – | | SIMD-Array | ''parallel Array'' | Heap (aligned) | – | | Assoziativ | ''Map'' | Heap | – | | Werttyp | ''struct'' | Stack | – | | Referenztyp | ''class'' | Heap | GPR (Pointer) | | Aufzählung | ''enum'' | Code (Immediate) | GPR | | Bereichstyp | ''type X = intN range a..b'' | wie Basistyp | wie Basistyp | | Wildcard | ''any'' | – | – (Generics) | ===== 18. Implementierungsstatus (v0.9.0) ===== ^ Feature ^ Status ^ | Alle Integer-Typen (int8–int64, uint8–uint64) | ✅ Vollständig | | f32, f64 (IEEE 754) | ✅ Vollständig | | bool, char, pchar | ✅ Vollständig | | qbool + QBool-Operationen | ✅ Vollständig | | ''[N]T'' feste Arrays (Fat Pointer) | ✅ Vollständig | | ''array'' dynamische Arrays | ✅ Vollständig | | ''parallel Array'' (SIMD) | ✅ Vollständig | | ''Map'' Hash-Map | ✅ Vollständig | | Range-Typen (Compile-Zeit + Laufzeit) | ✅ Vollständig | | struct, class, enum | ✅ Vollständig | | isize, usize | ✅ Vollständig | | Typ-Inferenz | ✅ Vollständig | | ''as''-Cast (alle Kombinationen) | ✅ Vollständig | | ''is''-Typ-Test | ✅ Vollständig | | ''any''-Wildcard (Generics) | ⚠️ Partial | | Aerospace-Garantien (Range-Typen, @redundant) | ✅ Stabil | | Cross-Arch (x86_64, ARM64, RISC-V64) | ✅ Vollständig | **Weiterführende Seiten:** * [[lyx_-_programmiersprache:syntax|Syntax-Referenz – Operatoren, Literale, Speicherklassen]] * [[lyx_-_programmiersprache:memory-management|Memory Management – Stack, Heap, std.alloc]] * [[lyx_-_programmiersprache:pointer-inlining|Pointer & Inlining – ^T, Dereferenz, @volatile]] * [[lyx_-_programmiersprache:generics-traits|Generics & Traits – Typen mit Schranken]] * [[lyx_-_programmiersprache:exception-handling|Fehlerbehandlung – SafeAdd, Result, Option]] * [[lyx_-_programmiersprache:do-178c|DO-178C – Range-Typen und Datensicherheit]] * [[lyx_-_programmiersprache:das-energy-aware-programmiermodell|Energy-Aware – qbool und QBool-Operationen]]