====== Lyx Programming Language ======
> Native • Statisch typisiert • Multi-Plattform • Embedded • Aerospace • High Performance
^ Version | **v0.8.5-aerospace** |
^ Compiler | ''lyxc'' |
^ Status | Aktive Entwicklung |
^ Autor | Andreas Röne |
-----
===== Was ist Lyx? =====
Lyx ist eine moderne, nativ kompilierende Programmiersprache, die von Grund auf für zwei Welten entworfen wurde: leistungsstarke Systemprogrammierung und zertifizierungsfähige, sicherheitskritische Software nach **DO-178C**.
Der Compiler erzeugt direkt ausführbare Programme für x86_64, ARM64, RISC-V und ESP32 — ohne virtuelle Maschine, ohne Bytecode und ohne externe Runtime-Abhängigkeiten. Alle Standardbibliotheken (std/ und data/) sind in nativem Lyx geschrieben; es existieren keine Abhängigkeiten zu C-Bibliotheken oder anderen Laufzeitsystemen.
Für sicherheitskritische Anwendungen bietet Lyx eine integrierte Safety-Toolchain: Sicherheits-Pragmas (@flight_crit, @redundant, @stack_limit, @wcet, …), Range-Types mit Compile-Time- und Runtime-Prüfung, MC/DC-Instrumentierung und deterministische, reproduzierbare Builds. Diese Eigenschaften machen Lyx zu einer geeigneten Basis für Software nach **DAL-A** bis **DAL-E** gemäß DO-178C.
===== Hauptmerkmale =====
* Native Codegenerierung (ELF, PE, Mach-O) für x86_64, ARM64, RISC-V, ESP32
* Statische Typisierung ohne implizite Konvertierungen
* Deterministische, reproduzierbare Builds
* Multi-Plattform-Kompilierung aus einer Codebasis
* Module und vorkompilierte Units (.lyu)
* Generics mit Monomorphisierung (WCET-berechenbar)
* Structs, Klassen, virtuelle Methoden und Vererbung
* Pattern Matching
* Nullable Types
* Tuple Returns
* SIMD-Unterstützung
* Umfangreiche Standardbibliothek (std/) und Daten-Bibliothek (data/)
* **Safety-Toolchain**: MC/DC-Instrumentierung, Coverage Reports, Statische Analyse, Call-Graph, Stack-Check, Provenance Tracking
* **DO-178C-orientierte Pragmas**: @flight_crit, @redundant (TMR), @stack_limit, @wcet, @integrity, @volatile, @packed, @dal
* Range-Types mit Compile-Time- und Runtime-Bereichsprüfung
* Keine externen Abhängigkeiten — vollständig in Lyx implementiert
-----
===== Hello World =====
fn main(): int64 {
PrintStr("Hello Lyx!\n");
return 0;
}
Kompilieren:
lyxc hello.lyx -o hello
./hello
-----
===== Sprachüberblick =====
==== Variablen ====
Lyx unterscheidet vier Speicherklassen, weil Veränderbarkeit eine bewusste Entscheidung sein soll — nicht eine Standardannahme. ''var'' markiert eine veränderbare Variable, ''let'' ist nach der ersten Zuweisung unveränderbar, ''co'' ist ein schreibgeschützter Stack-Slot, dessen Wert erst zur Laufzeit feststeht, und ''con'' ist eine echte Compile-Zeit-Konstante, die direkt als Immediate in den Maschinencode eingebettet wird.
Der Vorteil ist doppelt: Der Compiler kann Optimierungen aggressiver anwenden, wenn er weiß, was sich nie ändert. Und im Code ist auf den ersten Blick erkennbar, welche Daten fließen und welche feststehen — das ist besonders in sicherheitskritischem Code und bei Code-Reviews von hohem Wert.
var counter: int64 := 42;
let maximum: int64 := 100;
co runtimeConst: int64 := 5;
con compileConst: int64 := 10;
==== Funktionen ====
Funktionen in Lyx verlangen explizite Typangaben für alle Parameter und den Rückgabetyp. Das ist eine bewusste Entscheidung gegen vollständige Typ-Inferenz bei Schnittstellen: Eine Funktion ist ein Vertrag zwischen Aufrufer und Implementierung, und dieser Vertrag soll im Quellcode lesbar sein — ohne Compiler-Lauf oder IDE.
Das erleichtert Code-Reviews, macht Schnittstellen selbstdokumentierend und ist Voraussetzung für DO-178C-konforme Softwarenachweise, bei denen Schnittstellenspezifikationen prüfbar dokumentiert sein müssen.
fn add(a: int64, b: int64): int64 {
return a + b;
}
→ [[lyx_-_programmiersprache:funktionen|Funktionen (vertieft) — Tupel-Rückgabe, anonyme Funktionen, Higher-Order Functions, Pipe-Operator]]
==== Bedingungen ====
Bedingungen nutzen die bekannte if/else-Struktur mit Klammerung der Bedingung. Lyx trennt strikt zwischen Zuweisung ('' := '') und Vergleich ('' == ''), was den häufigen C-Fehler ''if (x = 0)'' strukturell unmöglich macht — eine Zuweisung ist kein gültiger Ausdruck in einer Bedingung.
Das ist kein stilistisches Detail, sondern ein sicherheitsrelevantes Design: In Systemen, die nach DO-178C zertifiziert werden, ist jede Quelle versehentlicher Seiteneffekte in Bedingungen ein potenzieI kritischer Fehler.
if (value > 0) {
PrintStr("positive\n");
}
else {
PrintStr("negative\n");
}
==== Schleifen ====
''while'' ist die grundlegende Schleifenform für bedingte Wiederholung. Lyx bietet zusätzlich ''for i := 1 to N'' und ''repeat ... until'' für häufige Muster. Die explizite Abbruchbedingung ist bewusst sichtbar gehalten — kein implizites Iterator-Protokoll, das versteckt, wann die Schleife endet.
In sicherheitskritischer Software ist die Endlichkeit von Schleifen eine Nachweispflicht. Explizite Bedingungen vereinfachen die Worst-Case-Execution-Time-Analyse (WCET) erheblich, weil das Analysetool die Bedingung direkt auswerten kann.
while (i < 10) {
PrintInt(i);
i := i + 1;
}
==== Arrays ====
Arrays in Lyx sind Fat Pointers: Sie speichern intern Basisadresse, aktuelle Länge und Kapazität. Der Zugriff per Index ist syntaktisch direkt, und Zuweisungen über den Index sind klar von Lesezugriffen unterscheidbar. Dynamische Arrays wachsen bei Bedarf automatisch nach.
Für sicherheitskritische Systeme empfehlen sich statische Arrays mit fester Größe (''var buf: uint8[256]''), da diese vollständig auf dem Stack liegen und keinen Heap-Druck erzeugen. Dynamische Arrays sind ideal für Server- und Tool-Code, wo Flexibilität wichtiger ist als deterministisches Speicherverhalten.
var values := [10, 20, 30];
PrintInt(values[0]);
values[1] := 50;
==== Enums ====
Enums bündeln zusammengehörige benannte Ganzzahl-Konstanten unter einem gemeinsamen Typ. Anstatt "Magic Numbers" wie ''0'', ''1'', ''2'' im Code zu verwenden, gibt es sprechende Namen wie ''Status::Ok'' oder ''Status::Error''. Intern werden Enum-Werte als ''int64'' abgebildet, was die FFI-Nutzung und das Logging vereinfacht.
Der wesentliche Vorteil ist Lesbarkeit und Refactoring-Sicherheit: Wenn sich ein Wert ändert, ändert man ihn an einer Stelle — nicht an zwanzig Stellen im Code, an denen ''2'' für "Error" stand.
enum Status {
Ok,
Warning,
Error
}
var s: int64 := Status::Ok;
==== Tuple-Rückgaben ====
Manchmal muss eine Funktion mehrere zusammengehörige Werte gleichzeitig zurückgeben — zum Beispiel Quotient und Rest einer Division, oder einen Wert zusammen mit einem Fehlercode. Ohne Tuple-Rückgaben wären dafür entweder Output-Parameter (unschön) oder ein eigens definierter Wrapper-Struct (Overhead) nötig.
Lyx erlaubt Tupel direkt in der Funktionssignatur und destrukturiert sie bei der Zuweisung. Das Ergebnis ist kompakter, selbsterklärender Code ohne zusätzliche Typdefinitionen für einfache Multi-Rückgaben.
fn divmod(a: int64, b: int64): (int64, int64) {
return (a / b, a % b);
}
var quotient, remainder := divmod(17, 5);
→ [[lyx_-_programmiersprache:funktionen|Funktionen (vertieft) — vollständige Dokumentation zu Tupel-Rückgabe und Destrukturierung]]
==== Structs ====
Structs sind leichtgewichtige, stackbasierte Datencontainer. Sie können Methoden besitzen, unterstützen aber keine Vererbung. Zuweisung kopiert den gesamten Inhalt (Copy-by-Value) — es gibt keinen verborgenen Heap-Zugriff und keine Referenzzählung.
Das ist die bevorzugte Datenstruktur für kleine, kurzlebige Objekte wie Vektoren, Punkte, Farben oder Sensorwerte. Zero-Overhead, kein GC-Druck, vollständig deterministisch — genau das, was Embedded- und Aerospace-Code verlangt.
type Point = struct {
x: int64;
y: int64;
fn lengthSquared(): int64 {
return self.x * self.x +
self.y * self.y;
}
};
==== Klassen ====
Klassen sind für Objekte, die Polymorphismus, Vererbung und eine längere Lebensdauer als ihren Erzeugungskontext brauchen. Sie werden auf dem Heap angelegt (''new'') und müssen explizit freigegeben werden (''dispose''). Virtuelle Methoden ermöglichen, dass verschiedene Untertypen austauschbar verwendet werden können.
Lyx trennt bewusst Structs und Klassen, damit die Performance-Implikationen — Heap-Allokation und V-Table-Lookup — im Code sichtbar bleiben. In DAL-A Systemen sollten Klassen nur in der Initialisierungsphase instanziiert werden, nicht im laufenden Regelzyklus.
type Animal = class {
virtual fn speak() {
PrintStr("sound\n");
}
};
type Dog = class extends Animal {
override fn speak() {
PrintStr("woof\n");
}
};
==== Generics ====
Generics ermöglichen es, eine Funktion oder Datenstruktur einmal zu schreiben und für beliebige Typen zu verwenden. Der Compiler erzeugt per Monomorphisierung für jeden genutzten Typ eine eigene optimierte Version — es gibt keinen Runtime-Overhead, keine Typ-Erasure und keine Boxed Values wie in manchen anderen Sprachen.
Das Ergebnis ist DRY-Code (Don't Repeat Yourself) ohne Kompromisse bei der Performance. Für Lyx besonders relevant: Statische Generics sind WCET-berechenbar, weil alle Sprungziele zur Compile-Zeit bekannt sind.
fn max[T](a: T, b: T): T {
if (a > b) {
return a;
}
return b;
}
==== Pattern Matching ====
Pattern Matching ist eine leistungsfähigere Fallunterscheidung als eine Kette aus if/else-Blöcken. In einem einzigen ''match''-Ausdruck lassen sich Einzelwerte, mehrere Werte (''1 | 2'') und Wertebereiche (''100..199'') kompakt und übersichtlich abdecken. Der ''default''-Zweig behandelt alle nicht explizit genannten Fälle.
Der Vorteil gegenüber langen if-Ketten: Der Code bleibt flach statt verschachtelt, der Compiler kann warnen, wenn Fälle fehlen, und die Lesbarkeit steigt deutlich — besonders bei Zustandsautomaten, Protokoll-Parsern und Fehlerbehandlung.
match (value) {
case 0 => PrintStr("zero\n");
case 1 | 2 => PrintStr("small\n");
default => PrintStr("other\n");
}
==== Pipe Operator ====
Der Pipe-Operator ''|>'' leitet den Rückgabewert eines Ausdrucks als ersten Parameter an die nächste Funktion weiter. Das ersetzt tief verschachtelte Ausdrücke wie ''addOne(double(5))'' durch eine lineare, von oben nach unten lesbare Abfolge von Transformationsschritten.
Das ist besonders nützlich bei Datenpipelines: Eingabewert aufbereiten, filtern, umrechnen, ausgeben — jeder Schritt ist eine eigene Zeile, die Reihenfolge ist sofort ersichtlich. Im Energy-Aware-Modell kann der Compiler solche Pipelines zusätzlich auf Instruktionsbasis optimieren.
→ [[lyx_-_programmiersprache:funktionen|Funktionen (vertieft) — Pipe-Operator, Higher-Order Functions und anonyme Funktionen]]
fn double(x: int64): int64 {
return x * 2;
}
fn addOne(x: int64): int64 {
return x + 1;
}
var result := 5
|> double()
|> addOne();
-----
===== Datentypen =====
Lyx ist statisch und stark typisiert: Jeder Wert hat zur Compile-Zeit einen festen Typ, und implizite Konvertierungen zwischen Typen gibt es nicht. Wer einen Wert in einen anderen Typ umwandeln will, muss das explizit mit dem ''as''-Operator tun. Das verhindert eine ganze Klasse von Fehlern, die in schwach typisierten Sprachen erst zur Laufzeit sichtbar werden.
==== Ganzzahlen ====
Lyx bietet signierte und unsignierte Ganzzahlen in vier Breiten. Der Standard-Ganzzahltyp ist ''int64'' — auf modernen 64-Bit-Architekturen (x86_64, ARM64, RISC-V) entspricht das der nativen Registergröße und verursacht keinen Overhead. Kleinere Typen (''int8'', ''uint16'' etc.) werden verwendet, wenn Speicherplatz oder das genaue Bit-Layout eine Rolle spielen — zum Beispiel beim Zugriff auf Hardware-Register, beim Parsen von Netzwerkprotokollen oder beim Befüllen von Puffern.
Unsignierte Typen eignen sich überall dort, wo negative Werte konzeptionell ausgeschlossen sind: Array-Indizes, Bitmaps, Byte-Felder, Speicheradressen. Der Compiler kann für unsignierte Typen bestimmte Optimierungen aggressiver anwenden, weil kein Überlaufverhalten für negative Zahlen berücksichtigt werden muss.
Jeder Ganzzahltyp hat ein eigenes Literal-Suffix, sodass der Typ bereits im Quellcode erkennbar ist, ohne den Kontext lesen zu müssen.
^ Typ ^ Bits ^ Vorzeichen ^ Typisches Einsatzgebiet ^
| ''int8'' | 8 | signiert | Kompakte signierte Werte, Temperaturoffsets |
| ''int16'' | 16 | signiert | Audio-Samples (PCM), kleine Koordinaten |
| ''int32'' | 32 | signiert | Kompatibilität mit C-APIs, POSIX-Codes |
| ''int64'' | 64 | signiert | Standard — Zähler, Berechnungen, IDs |
| ''int'' | 64 | signiert | Alias für int64 |
| ''uint8'' | 8 | unsigniert | Bytes, Protokollfelder, Pixel-Kanäle |
| ''uint16'' | 16 | unsigniert | Ports, Unicode-Codepoints, kleine Adressen |
| ''uint32'' | 32 | unsigniert | IPv4-Adressen, CRC-Werte, Flags |
| ''uint64'' | 64 | unsigniert | Bitmaps, Speicheradressen, große Zähler |
var temperature: int8 := -12i8;
var port: uint16 := 8080u16;
var checksum: uint32 := 0xDEADBEEFu32;
var counter: int64 := 1_000_000; // Unterstriche zur Lesbarkeit erlaubt
==== Plattformabhängige Ganzzahlen ====
''isize'' und ''usize'' passen sich der nativen Pointer-Größe der Zielarchitektur an — 64 Bit auf x86_64 und ARM64, 32 Bit auf ESP32 (Xtensa). Sie sind die richtige Wahl für Array-Indizes und Pointer-Arithmetik, weil sie garantieren, dass kein Wertebereich-Überlauf entstehen kann, der nur auf bestimmten Plattformen auftritt.
^ Typ ^ Beschreibung ^ Verwendung ^
| ''isize'' | Vorzeichenbehaftete Pointer-Größe | Pointer-Differenzen, Offsets |
| ''usize'' | Vorzeichenlose Pointer-Größe | Array-Indizes, Längen, Kapazitäten |
==== Fließkomma ====
Lyx unterstützt zwei Fließkommatypen nach IEEE 754. ''f64'' ist der Standard — er bietet ausreichend Präzision für nahezu alle Berechnungen und entspricht dem, was moderne FPUs nativ verarbeiten. ''f32'' wird verwendet, wenn Speicherplatz oder Bandbreite knapp ist, zum Beispiel in großen SIMD-Arrays oder bei der Übertragung von Sensordaten über ressourcenbeschränkte Verbindungen.
^ Typ ^ Bits ^ Präzision ^ Typisches Einsatzgebiet ^
| ''f32'' | 32 | ~7 Dezimalstellen | SIMD-Berechnungen, Grafik, kompakte Puffer |
| ''f64'' | 64 | ~15 Dezimalstellen | Wissenschaftliche Berechnungen, Standard |
In sicherheitskritischen Systemen nach DO-178C müssen Fließkommaberechnungen besonders sorgfältig validiert werden, da Rundungsfehler akkumulieren können. Lyx bietet keine Fließkommaliterale ohne explizite Markierung — ''3.14'' ist immer ''f64'', ''3.14f32'' ist ''f32''.
var pi: f64 := 3.14159265358979;
var ratio: f32 := 0.5f32;
var altitude: f64 := sensor_read() as f64; // expliziter Cast
==== Zeichen und Strings ====
Lyx unterscheidet zwei Zeichentypen mit unterschiedlichem Abstraktionsniveau. ''char'' repräsentiert ein einzelnes ASCII-Zeichen (8 Bit). ''pchar'' ist ein nullterminierter Zeiger auf einen Zeichenpuffer — das direkte Äquivalent zu ''char*'' in C. Das macht FFI-Aufrufe und die Arbeit mit Systemschnittstellen reibungslos, weil keine automatische Konvertierung stattfindet.
Der höhere ''string''-Typ (aus ''std.string'') bietet dynamische Länge, UTF-8-Unterstützung und sichere Operationen auf Kosten eines kleinen Overhead. Für Systemprogrammierung und Protokoll-Implementierungen ist ''pchar'' oft die bessere Wahl, weil er direkt in Netzwerkpuffer oder Speicherbereiche zeigen kann, ohne Kopien zu erzeugen.
^ Typ ^ Beschreibung ^ Verwendung ^
| ''char'' | Einzelnes ASCII-Zeichen | Zeichenverarbeitung, Protokoll-Bytes |
| ''pchar'' | Nullterminierter C-String-Pointer | FFI, Systemaufrufe, Puffer-Referenzen |
| ''string'' | Dynamischer High-Level-String | Textverarbeitung, User-Interfaces |
var initial: char := 'A';
var label: pchar := "Sensor-01"; // String-Literal ist pchar
var message: string := String("Hallo"); // std.string High-Level-Typ
==== Wahrheitswert ====
''bool'' nimmt genau zwei Werte an: ''true'' und ''false''. Anders als in C gibt es keine implizite Konvertierung von Ganzzahlen zu ''bool'' — ein ''int64'' ist kein ''bool'', auch wenn sein Wert 0 oder 1 ist. Das verhindert einen häufigen Fehler, bei dem Rückgabewerte von Funktionen (die eigentlich Fehlercodes sind) versehentlich als Wahrheitswerte interpretiert werden.
var active: bool := true;
if (active) {
PrintStr("System läuft\n");
}
// Fehler: implizite Konvertierung nicht erlaubt
// if (getSensorValue()) { ... } // Kompilierungsfehler
// Richtig: expliziter Vergleich
if (getSensorValue() > 0) { ... }
==== Enums ====
Enums gruppieren zusammengehörige benannte Ganzzahl-Konstanten. Intern werden sie als ''int64'' abgebildet, was FFI-Kompatibilität und direktes Logging ohne Konvertierung sicherstellt. Ausführliche Erklärung: [[#Enums|Sprachüberblick → Enums]].
enum Direction { North, South, East, West }
var heading: int64 := Direction::North;
==== Tuple ====
Ein Tupel fasst mehrere Werte unterschiedlicher Typen zu einer temporären Einheit zusammen, ohne dafür einen benannten Struct definieren zu müssen. Haupteinsatzgebiet sind Mehrfach-Rückgaben aus Funktionen. Ausführliche Erklärung: [[#Tuple-Rückgaben|Sprachüberblick → Tuple-Rückgaben]].
fn minMax(a: int64, b: int64): (int64, int64) {
if (a < b) { return (a, b); }
return (b, a);
}
var lo, hi := minMax(42, 17);
==== Arrays ====
Lyx kennt zwei Array-Formen. Dynamische Arrays (''array'') sind Fat Pointer mit automatischer Kapazitätsverwaltung — sie wachsen bei Bedarf nach und speichern intern Länge und Kapazität. Statische Arrays (''var buf: uint8[256]'') haben eine feste, zur Compile-Zeit bekannte Größe und liegen vollständig auf dem Stack — kein Heap, keine Allokation, deterministisches Verhalten.
Für Embedded- und Safety-Code sind statische Arrays vorzuziehen, weil ihre Speichergröße zur Compile-Zeit prüfbar ist. Dynamische Arrays sind das Mittel der Wahl für Server- und Tool-Code, wo Flexibilität wichtiger ist.
''parallel Array'' ist eine SIMD-optimierte Variante: Der Compiler legt die Daten so im Speicher an, dass Vektoroperationen (SSE, AVX, NEON) ohne manuelle Intrinsics genutzt werden können.
^ Typ ^ Speicher ^ Größe ^ Verwendung ^
| ''array'' | Heap | Dynamisch | Allgemeine Listen, Puffer |
| ''var buf: T[N]'' | Stack | Compile-Zeit-Konstant | Embedded, Safety-Critical |
| ''parallel Array'' | Heap (SIMD-aligned) | Dynamisch | Batch-Berechnungen, Signalverarbeitung |
var list: array := [1, 2, 3, 4, 5];
list.push(6); // wächst automatisch nach
var buf: uint8[256]; // statischer Stack-Puffer
buf[0] := 0xAA;
var signal := parallel Array(1024); // SIMD-optimiertes Array
==== Structs und Klassen ====
Structs sind stackbasierte Value-Types (Copy-by-Value, keine Vererbung), Klassen sind heapbasierte Reference-Types mit Vererbung und virtuellen Methoden. Die bewusste Trennung macht die Performance-Implikationen im Code sichtbar. Ausführliche Erklärung: [[#Structs|Sprachüberblick → Structs]] und [[#Klassen|Klassen]].
==== Range-Typen ====
Range-Typen sind eines der markantesten Features von Lyx für sicherheitskritische Software. Sie definieren einen Ganzzahltyp mit einem fest eingeschränkten Wertebereich. Der Compiler prüft konstante Zuweisungen zur Compile-Zeit und fügt für dynamische Zuweisungen automatisch Laufzeit-Checks ein.
Das Ziel ist, ungültige Systemzustände unmöglich zu machen — nicht durch nachträgliche Validierung, sondern durch das Typsystem selbst. Eine Variable vom Typ ''Altitude'' kann schlicht keinen Wert außerhalb von -1000 bis 60000 annehmen. Das ist eine zentrale Anforderung der DO-178C für Avionik-Software.
type Altitude = int64 range -1000..60000; // Meter über NN
type Speed = int64 range 0..300; // km/h
type Percent = int64 range 0..100;
var height: Altitude := 10500; // OK
var speed: Speed := 350; // Kompilierungsfehler: außerhalb des Bereichs
fn setThrottle(p: Percent) {
// p kann hier niemals < 0 oder > 100 sein — garantiert durch den Typ
}
==== QBool — Probabilistischer Wahrheitswert ====
''qbool'' ist ein Typ, der keinen binären Wahrheitswert speichert, sondern eine Wahrscheinlichkeit zwischen 0.0 und 1.0. Das Literal dafür ist ein Gleitkommazahl mit dem Suffix ''q''. Er wird im Energy-Aware-Modell eingesetzt: Bei niedrigen Energy-Levels (''@energy(1)'') kann der Compiler ''qbool''-Entscheidungen probabilistisch abkürzen, statt teure exakte Vergleiche durchzuführen.
Praktisch ist ''qbool'' für heuristische Filter, unsichere Sensorfusionen oder KI-gestützte Entscheidungen, bei denen ein "wahrscheinlich wahr" ausreicht und der Energieverbrauch eines exakten Ergebnisses nicht gerechtfertigt ist.
var confidence: qbool := 0.85q; // 85 % Wahrscheinlichkeit
if (confidence) {
// wird ausgeführt, wenn der Wert über dem internen Schwellenwert liegt
ProcessResult();
}
==== Typkonvertierung ====
Lyx erlaubt keine impliziten Typkonvertierungen. Jede Umwandlung muss mit dem ''as''-Operator explizit ausgedrückt werden. Das macht an jeder Stelle im Code sichtbar, dass eine Konvertierung stattfindet — und ob dabei Präzision verloren gehen kann.
var x: int64 := 42;
var y: f64 := x as f64; // int64 → f64, verlustfrei
var z: int32 := x as int32; // int64 → int32, möglicher Wertebereichsverlust
var pi: f64 := 3.14;
var n: int64 := pi as int64; // f64 → int64: Nachkommastellen werden abgeschnitten
==== void ====
''void'' ist der Rückgabetyp für Funktionen, die keinen Wert zurückgeben. Er ist kein echter Datentyp und kann nicht als Variablentyp verwendet werden.
fn resetCounter(): void {
counter := 0;
}
-----
===== Modulsystem =====
Das Modulsystem von Lyx basiert auf dem Konzept der **Units**: Jede Quelldatei ist eine eigenständige Einheit mit einem expliziten Namen, einer definierten öffentlichen Schnittstelle und klaren Abhängigkeiten. Keine impliziten Includes, keine globalen Header — jede Abhängigkeit ist im Quellcode deklariert und für den Compiler nachvollziehbar.
==== Unit-Deklaration ====
Jede Lyx-Quelldatei beginnt mit einer ''unit''-Deklaration. Sie legt den Namen der Unit fest, unter dem sie von anderen Dateien importiert werden kann. Ohne diese Deklaration ist die Datei für das Modulsystem nicht sichtbar.
Die Deklaration hat keinen Einfluss auf den Dateinamen — sie ist eine logische Zuordnung, die der Compiler für die Abhängigkeitsauflösung und für Fehlermeldungen verwendet.
unit MathHelpers;
// Ab hier folgen Typen, Konstanten und Funktionen dieser Unit.
==== Sichtbarkeit: pub ====
Standardmäßig ist alles, was in einer Unit definiert wird, nur innerhalb dieser Unit sichtbar. Wer eine Funktion, einen Typ oder eine Konstante nach außen exportieren will, markiert sie explizit mit ''pub''.
Das ist eine bewusste Entscheidung gegen das C-Modell, bei dem alles in einem Header standardmäßig öffentlich ist. In Lyx ist die öffentliche Schnittstelle einer Unit das, was mit ''pub'' markiert ist — nicht mehr und nicht weniger. Das erzwingt saubere Kapselung und macht die API einer Unit auf einen Blick erkennbar.
unit MathHelpers;
pub fn square(x: int64): int64 {
return x * x;
}
fn internalHelper(x: int64): int64 { // nur intern sichtbar
return x * 2;
}
==== Einzelne Units importieren ====
Eine Unit wird über ihren vollständig qualifizierten Namen importiert. Der Compiler sucht die entsprechende Datei anhand dieses Namens im konfigurierten Suchpfad. Nach dem Import stehen alle ''pub''-Symbole der Unit direkt zur Verfügung.
import std.math;
import std.string;
import std.net.http;
==== Namespace-Import (Wildcard) ====
Statt einzelner Units kann auch ein ganzer Namespace auf einmal importiert werden. Das ist sinnvoll, wenn mehrere Units eines Pakets gemeinsam verwendet werden — zum Beispiel alle Audio-Untermodule oder alle Netzwerkprotokolle eines Projekts.
Der Wildcard-Import zieht alle Units des Namespace in den aktuellen Scope. Er sollte mit Bedacht eingesetzt werden: In großen Projekten kann er die Abhängigkeitsgraphen unübersichtlich machen, in kleineren Scripts oder Prototypen ist er sehr praktisch.
import std.audio.*; // lädt std.audio, std.audio.alsa, std.audio.mpg123, std.audio.playback
import std.net.*; // lädt alle 21 Net-Units auf einmal
import std.validate.*; // lädt EAN, IBAN, ISBN, Luhn, VAT
==== Vorkompilierte Units (.lyu) ====
Units lassen sich mit dem Compiler in ein binäres Zwischenformat übersetzen. Diese vorkompilierten Units tragen die Dateiendung ''.lyu'' und enthalten bereits das interne Lyx-IR — der Compiler muss sie beim nächsten Build nicht mehr parsen und analysieren, sondern kann sie direkt einbinden.
Das beschleunigt die Kompilierung großer Projekte erheblich, weil stabile Dependencies (eigene Bibliotheken, externe Pakete) nur einmal kompiliert werden müssen. Beim Import prüft der Compiler zuerst, ob eine ''.lyu''-Version der angeforderten Unit existiert. Findet er sie, wird diese bevorzugt geladen. Nur wenn keine ''.lyu''-Datei vorhanden ist, fällt er auf die ''.lyx''-Quelldatei zurück.
Unit erzeugen:
lyxc std/math.lyx --compile-unit -o std/math.lyu
Beim nächsten Build wird ''math.lyu'' automatisch bevorzugt:
import std.math; // lädt math.lyu, falls vorhanden — sonst math.lyx
Metadaten einer vorkompilierten Unit anzeigen:
lyxc --unit-info std/math.lyu
==== Eigene Bibliotheken einbinden ====
Der Compiler sucht Units standardmäßig im aktuellen Verzeichnis und in den Standardpfaden der Standardbibliothek. Eigene Bibliotheken oder Drittanbieter-Units werden über den ''-I''-Flag in den Suchpfad aufgenommen. Mehrere Pfade sind möglich.
lyxc app.lyx -I ./libs -I ./vendor -I /opt/lyx/extra
==== Abhängigkeiten analysieren ====
Mit ''--trace-imports'' gibt der Compiler während der Kompilierung aus, welche Units er in welcher Reihenfolge lädt und ob er dabei auf eine ''.lyx''- oder ''.lyu''-Version zurückgreift. Das ist nützlich beim Debugging von Abhängigkeitsproblemen oder beim Aufspüren unnötiger Imports.
lyxc app.lyx --trace-imports
-----
===== Standardbibliothek =====
Die Lyx Standard Library umfasst über 84 dokumentierte Units in 17 Kategorien — von Speicherverwaltung und Betriebssystem-Abstraktion über Kryptographie und Netzwerkprotokolle bis hin zu Machine Learning und Audioverarbeitung.
Das zentrale Designprinzip ist **Zero External Dependencies**: Alle Units sind in nativem Lyx implementiert und brauchen keine externen C-Bibliotheken oder Treiber. Datenbankclients implementieren die Protokolle direkt über TCP. Netzwerk-Units sprechen die Protokolle nativ, ohne auf libcurl, OpenSSL oder ähnliche Abhängigkeiten zu setzen. Das hält Deployments minimal, Builds deterministisch und ermöglicht Cross-Compilation auf Embedded-Targets wie ESP32 oder RISC-V, wo externe Bibliotheken oft nicht verfügbar sind.
==== Kern & Laufzeit ====
Der Kern der Standardbibliothek ist bewusst klein gehalten. ''std.system'' liefert grundlegende Systemkonstanten und Plattform-Identifikation. ''std.alloc'' stellt Heap-Allokation mit POSIX-Alignment bereit — die Basis für alle dynamischen Datenstrukturen. ''std.error'' und ''std.result'' bieten typsichere Fehlerbehandlung ohne Exceptions: Funktionen geben Ergebnis-Typen zurück, die entweder einen Wert oder einen Fehlercode enthalten, ähnlich wie Rust's ''Result''. ''std.qbool'' implementiert den probabilistischen Wahrheitswert für das Energy-Aware-Programmiermodell.
==== Ein-/Ausgabe ====
''std.io'' ist die grundlegende I/O-Unit für Textausgabe und Lesen von Standardein-/ausgabe. ''std.crt'' und ''std.crt_raw'' bieten Terminal-Steuerung: Cursor-Positionierung, Farben, Zeichensätze — die Basis für LyxVision-Anwendungen. ''std.log'' implementiert strukturiertes Logging mit Levels (Debug, Info, Warning, Error) und konfigurierbarem Output. ''std.buffer'' stellt Byte-Puffer mit sicherem Lesen und Schreiben bereit. ''std.pack'' ermöglicht binäres Serialisieren und Deserialisieren nach definierten Layouts — essenziell für Protokoll-Implementierungen und Dateiformate.
==== Betriebssystem & Dateisystem ====
Diese Gruppe abstrahiert direkte Betriebssystem-Aufrufe. ''std.fs'' deckt Datei- und Verzeichnisoperationen ab. ''std.os'' gibt Zugriff auf Umgebungsvariablen, Signale und POSIX-Systemaufrufe. ''std.env'' liest Kommandozeilenargumente aus dem ''argv''-Array. ''std.process'' startet und verwaltet Kindprozesse. ''std.thread'' implementiert POSIX-Threads mit Mutex, Condition Variables, Thread-Local Storage und atomaren Operationen. ''std.time'' liefert Echtzeit-Uhren, monotone Zeitgeber und Zeitzonenunterstützung. ''std.systeminfo'' fragt Hardware-Parameter ab: CPU-Kerne, Speichergröße, Plattformname.
→ [[lyx_-_programmiersprache:threads-nebenlaeufikeit|Threads & Nebenläufigkeit – vollständige Dokumentation]] (POSIX / Linux / macOS)\\
→ [[lyx_-_programmiersprache:rtos-embedded-concurrency|Nebenläufigkeit auf Embedded-Targets & RTOS]] (Bare-Metal, ARM Cortex-M, RISC-V, ESP32/FreeRTOS)
==== Strings & Text ====
''std.string'' ist der High-Level-String-Typ mit dynamischer Länge, einem StringBuilder-Muster und UTF-8-Unterstützung. ''std.conv'' konvertiert zwischen Ganzzahlen, Fließkommazahlen und Strings in beide Richtungen, inklusive Hex- und Binärdarstellung. ''std.regex'' bietet reguläre Ausdrücke mit einer einfachen und einer kompilierten API für Performance-kritische Anwendungen.
Für strukturierte Datenformate gibt es ''std.json'', ''std.xml'', ''std.yaml'' und ''std.ini'' — alle mit Parser und Serialisierer. ''std.url'' parst und baut URLs nach RFC 3986. ''std.html'' generiert HTML und escaped Sonderzeichen. ''std.base64'' kodiert und dekodiert nach RFC 4648.
==== Datenstrukturen ====
''std.list'' bietet dynamische Listen, statische Listen fixer Größe (8 und 16 Elemente), einen LIFO-Stack und eine FIFO-Queue — alle typisiert auf ''int64''. ''std.vector'' implementiert zweidimensionale Vektoren (''Vec2'') mit vollständiger Vektor-Arithmetik; ''std.vector_batch'' verarbeitet Arrays von Vektoren SIMD-optimiert. ''std.sort'' stellt Sortieralgorithmen für Integer-Arrays bereit. ''std.hash'' ist die umfangreichste Unit der Standardbibliothek: Sie enthält über 20 Hash-Algorithmen — von schnellen nicht-kryptographischen Hashes (FNV-1a, DJB2, Murmur3, CRC32, xxHash, CityHash, FarmHash) über kryptographische Hashes (MD5, SHA-256, SHA-3, BLAKE3) bis zu Passwort-Hashing-Funktionen (bcrypt, Argon2, scrypt, PBKDF2).
==== Mathematik & Statistik ====
''std.math'' deckt trigonometrische, logarithmische und exponentielle Funktionen ab. ''std.math.constants'' stellt mathematische Konstanten wie π und e bereit. ''std.math_batch'' verarbeitet Arrays von Zahlen in einem einzigen Funktionsaufruf — SIMD-optimiert für Signalverarbeitung und numerische Simulation. ''std.stats'' berechnet deskriptive Statistiken (Mittelwert, Median, Varianz, Standardabweichung, Quantile, Korrelation). ''std.stats_batch'' tut dasselbe für große Datensätze mit Batch-Verarbeitung. ''std.units'' rechnet physikalische Einheiten um (Meter ↔ Fuß, Kelvin ↔ Celsius, Grad ↔ Radiant).
==== Machine Learning ====
''std.ml'' implementiert klassische ML-Algorithmen nativ: lineare und logistische Regression, k-Nearest Neighbors, Naive Bayes, Decision Trees, K-Means-Clustering — alles ohne externe Abhängigkeiten. ''std.fasttext'' ist eine native Lyx-Implementierung der FastText-Wortvektor-Engine (angelehnt an Facebook Research, 2016): Sie trainiert Skip-gram- und CBOW-Modelle per Stochastic Gradient Descent, erzeugt dichte Einbettungsvektoren (Standard: 100 Dimensionen) und findet semantisch ähnliche Wörter, löst Analogien und klassifiziert Texte.
→ [[lyx_-_programmiersprache:ml|Machine Learning – Detaildokumentation mit Beispielen]]
==== Netzwerk (std.net) ====
Der ''std.net''-Namespace ist der umfangreichste der Standardbibliothek mit 21 Units. Die Schichtung ist klar: ''std.net.syscalls'' und ''std.net.types'' bilden das Fundament, ''std.net.socket'' und ''std.net.tls'' die Transport-Schicht, und darüber liegen die Anwendungsprotokolle.
Alle Protokolle sind direkte Eigenimplementierungen — kein Wrapping von libcurl, libssh oder OpenSSL:
^ Protokoll ^ Unit ^ Besonderheit ^
| TCP/UDP/Raw | ''std.net.socket'' | 9 Socket-Typen inkl. ICMP, ARP, Unix Domain |
| TLS 1.2/1.3 | ''std.net.tls'' | Natives TLS ohne OpenSSL |
| DNS | ''std.net.dns'' | Vollständiger Resolver inkl. DNSSEC-Vorbereitung |
| HTTP/HTTPS | ''std.net.http'', ''std.net.https'' | Client-Implementierungen |
| SMTP / IMAP | ''std.net.smtp'', ''std.net.imap'' | E-Mail senden und empfangen |
| SSH | ''std.net.ssh'' | Verbindung, Authentifizierung, Tunneling |
| MQTT | ''std.net.mqtt'' | IoT-Messaging-Protokoll |
| QUIC | ''std.net.quic'' | UDP-basiertes Transportprotokoll |
| SIP | ''std.net.sip'' | VoIP-Signalisierung |
| LDAP | ''std.net.ldap'' | Verzeichnisdienste, Active Directory |
| SNMP | ''std.net.snmp'' | Netzwerkgeräte-Monitoring |
| BGP | ''std.net.bgp'' | Border Gateway Protocol |
| WHOIS | ''std.net.whois'' | Domain-Abfragen |
| NTP | ''std.net.ntp'' | Zeitsynchronisation |
| Telnet | ''std.net.telnet'' | Legacy-Terminal-Protokoll |
| MongoDB | ''std.net.mongo'' | Wire Protocol direkt über TCP |
| ASN.1 | ''std.net.asn1'' | Basis-Kodierung für LDAP und TLS |
==== Datenbanken (std.db) ====
''std.db.mysql'' implementiert das MySQL-Binärprotokoll (Protocol 41) vollständig über TCP — ohne externe Clientbibliothek. Es unterstützt Verbindungsaufbau mit Authentifizierung, SQL-Abfragen, strukturierte Ergebnisverarbeitung, Transaktionen und typsichere Prepared Statements. ''std.db.redis'' implementiert das RESP-Protokoll von Redis direkt. Beide Units brauchen keine installierten Datenbanktreiber, was sie besonders für Embedded-Server und minimale Deployments geeignet macht.
==== Kryptographie (std.crypto) ====
''std.crypto.aes'' implementiert AES-128 und AES-256 mit ECB- und CBC-Modus. ''std.crypto.sha1'' berechnet SHA-1-Hashes — primär für Protokoll-Kompatibilität (z.B. MySQL-Authentifizierung). Für moderne Kryptographie-Anforderungen stehen in ''std.hash'' SHA-256, SHA-3, BLAKE3 und die Passwort-Hashing-Funktionen bereit.
==== Audio (std.audio) ====
''std.audio'' ist die Basis-Unit mit Typen und Parsern für WAV- und MP3-Dateien sowie PCM-Konvertierungsfunktionen (8-Bit → 16-Bit, Stereo → Mono). ''std.audio.alsa'' bindet ALSA für Linux-Soundausgabe an. ''std.audio.mpg123'' nutzt libmpg123 für MP3-Dekodierung. ''std.audio.playback'' koordiniert die Wiedergabe-Pipeline.
==== Geometrie & Validierung ====
''std.geo'' implementiert geografische Berechnungen im WGS-84-System mit Mikro-Grad-Auflösung (1 Grad = 1.000.000 Einheiten): Haversine-Distanz, Peilwinkel, Bounding-Boxes, Punkt-in-Rechteck. ''std.rect'', ''std.circle'' und ''std.color'' decken 2D-Geometrie und Farbwerte ab.
''std.validate'' enthält Prüfroutinen für EAN-Barcodes, IBAN-Bankverbindungen, ISBN-Buchnummern, Luhn-Prüfziffern und europäische Umsatzsteuer-IDs (VAT) — nützlich für E-Commerce- und Finanzanwendungen.
→ Die vollständige Referenz aller Units mit Typen, Konstanten und Funktionssignaturen: **[[lyx_-_programmiersprache:units|Standardbibliothek – Vollständige Übersicht]]**
-----
===== Daten-Bibliothek (data/) =====
Neben dem ''std/''-Namespace gibt es einen eigenständigen ''data/''-Namespace mit einer Pandas-ähnlichen Datenanalyse-Bibliothek. Sie ist speziell auf tabellarische Datenverarbeitung, statistische Auswertungen und ETL-Pipelines ausgelegt und folgt dem gleichen Zero-Dependency-Prinzip wie die Standardbibliothek.
^ Unit ^ Beschreibung ^
| ''data.core'' | Kern-Datenstrukturen: ''Series'', ''DataFrame'', Statistik, GroupBy, Joins, boolesche Indizierung, Rolling Window, Normalisierung |
| ''data.io'' | CSV-Import (''ReadCSV'') und -Export (''WriteCSV'') |
| ''data.stats_batch'' | SIMD-bereite Batch-Statistikfunktionen für 4, 8 und 16 Werte direkt als Parameter |
Der zentrale Typ ist ''DataFrame'' — eine zweidimensionale Tabelle mit bis zu 16 benannten Spalten und 1024 Zeilen, typisiert über ''COL_INT64'', ''COL_FLOAT'', ''COL_STRING'' und ''COL_BOOL''. ''Series'' ist die eindimensionale Entsprechung. Beide unterstützen Aggregationen (Summe, Mittelwert, Median, Varianz, Korrelation), Filterung mit Vergleichsoperatoren und booleschen Masken, GroupBy, Joins (Inner/Left/Right/Outer), Pivot, Melt sowie Transformationen wie Rolling Mean, kumulative Summe und Z-Score-Normalisierung.
import data.core;
import data.io;
var df: DataFrame := ReadCSV("sales.csv");
// Zeilen filtern: score > 80
var high: DataFrame := DataFrameFilter(df, "score", OP_GT, 80);
// Statistik auf einer Spalte
var s: Series := DataFrameGetSeries(df, "revenue");
PrintInt(SeriesMean(s));
// Gruppieren und summieren
var gb: GroupByResult := DataFrameGroupBy(df, "region");
var totals: DataFrame := GroupBySum(gb, "revenue");
DataFramePrint(totals);
→ Vollständige Referenz mit allen Typen, Konstanten und Funktionen: **[[lyx_-_programmiersprache:data|Daten-Bibliothek – Vollständige Übersicht]]**
-----
===== LyxVision =====
LyxVision ist das integrierte Text-UI-Framework für terminalbasierte Anwendungen.
Verfügbare Komponenten:
* Fenster
* Dialoge
* Menüs
* Buttons
* Listen
* Eingabefelder
* Radiobuttons
* Terminal-Widgets
* Container-Gruppen
* Event-Loop
Beispiel:
import lyxvision.app;
import lyxvision.window;
-----
===== Qt5 Bindings =====
Für grafische Desktop-Anwendungen stehen Qt5-Bindings zur Verfügung:
* qt5_core
* qt5_app
* qt5_gl
* qt5_egl
* qt5_glx
-----
===== Compiler-Verwendung =====
==== Basis ====
lyxc program.lyx -o program
==== Cross-Compilation ====
lyxc app.lyx --target=win64
lyxc app.lyx --target=linux
lyxc app.lyx --target=arm64
lyxc app.lyx --target=macos-arm64
lyxc app.lyx --target=esp32
lyxc app.lyx --target=riscv
-----
===== Analyse- und Auditfunktionen =====
==== Codeanalyse ====
--lint
--lint-only
--static-analysis
--call-graph
==== Debugging ====
--emit-asm
--asm-listing
--dump-relocs
--trace-imports
==== Compiler-Introspection ====
--ast-dump
--symtab-dump
--trace-passes
--ir-source-map
--type-reasoning
--constraint-log
--provenance
==== Runtime-Sicherheit ====
--runtime-checks
==== Profiling ====
--profile
--trace
-----
===== Unit-Kompilierung =====
Vorkompilierte Units beschleunigen große Projekte.
Unit erzeugen:
lyxc math.lyx --compile-unit -o math.lyu
Informationen anzeigen:
lyxc --unit-info math.lyu
Debugsymbole einbetten:
lyxc math.lyx --compile-unit --debug-symbols -o math.lyu
-----
===== Aerospace & Safety =====
Lyx ist von Grund auf für den Einsatz in sicherheitskritischen Systemen ausgelegt. Die Sprache und ihr Compiler erfüllen die Anforderungen der DO-178C (Software Considerations in Airborne Systems and Equipment Certification) für die Entwicklungssicherheitsstufen **DAL-A** (Katastrophal) bis **DAL-E** (Kein Sicherheitseffekt).
Zentrale Bausteine sind dedizierte Safety-Pragmas (@flight_crit, @redundant für Triple Modular Redundancy, @stack_limit, @wcet, @integrity, @volatile, @packed, @dal), Range-Types mit automatischer Bereichsprüfung zur Compile-Zeit und zur Laufzeit, sowie eine vollständige Analyse-Toolchain mit MC/DC-Instrumentierung, statischer Analyse, Call-Graph-Erzeugung, Stack-Check und Provenance Tracking für auditierbare, reproduzierbare Builds.
→ [[lyx_-_programmiersprache:aerospace-safety|Aerospace & Safety – Vollständige Dokumentation]]\\
→ [[lyx_-_programmiersprache:do-178c|DO-178C Compliance — DAL-Stufen, MC/DC, WCET, TMR, Memory Scrubbing]]
-----
===== Unterstützte Plattformen =====
^ Plattform ^ Architektur ^
| Linux | x86_64 |
| Windows | x86_64 |
| macOS Intel | x86_64 |
| macOS Apple Silicon | ARM64 |
| Linux ARM64 | ARM64 |
| ESP32 | Xtensa |
| RISC-V | RV64 |
-----
===== Projektstatus =====
**Aktuelle Version:** v0.8.5-aerospace
Lyx verbindet moderne Sprachfeatures, native Multi-Plattform-Kompilierung, umfangreiche Standardbibliotheken und professionelle Analysewerkzeuge in einer einheitlichen Toolchain für Embedded-, System-, Server- und Aerospace-Software.