Inhaltsverzeichnis

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: 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: 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<T> – Dynamisches Array (Heap)

Größe wächst zur Laufzeit. Lebt auf dem Heap; muss explizit freigegeben werden.

var list: array<int64> := [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<T> verboten (Heap-Allokation). Dort ausschließlich [N]T-Arrays auf dem Stack verwenden.

parallel Array<T> – SIMD-optimiert

Für SIMD-Verarbeitung (AVX2, NEON) existiert eine spezielle Array-Form mit garantierter Ausrichtung:

var signal: parallel Array<f32>(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<T> Heap Dynamisch ✅ Ja ❌ Verboten
parallel Array<T> Heap (SIMD-aligned) Dynamisch ✅ Ja ❌ Verboten

10. Map<K, V> – Assoziatives Array

Map<K, V> ist eine Hash-Map mit typisiertem Schlüssel und Wert. Literale werden mit {key: value}-Syntax geschrieben.

var scores: Map<pchar, int64> := {
    "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.

</code>

===== 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)
intNuintN Bit-Reinterpretation (kein Wert-Check)
intNf64 Exakte Konvertierung (bis ±2⁵³)
f64intN Abschneiden Richtung Null (floor toward zero)
f32f64 Präzisions-Erweiterung (verlustfrei)
f64f32 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: 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<T> Heap
SIMD-Array parallel Array<T> Heap (aligned)
Assoziativ Map<K,V> 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<T> dynamische Arrays ✅ Vollständig
parallel Array<T> (SIMD) ✅ Vollständig
Map<K,V> 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: