====== Lyx – Erste Schritte ====== Diese Anleitung führt vom leeren Terminal bis zum ersten lauffähigen Lyx-Programm. Keine Vorkenntnisse in Lyx erforderlich — Grundkenntnisse in einer beliebigen Programmiersprache reichen. ---- ===== 1. Installation ===== Der Lyx-Compiler ''lyxc'' ist ein einzelnes, autarkes Binary. Er benötigt keine externe C-Runtime, keinen installierten Linker und keine Paketabhängigkeiten. ==== Linux / macOS ==== # Binary herunterladen und ausführbar machen curl -L https://seolizer.de/lyx/releases/latest/lyxc-linux-x86_64 -o lyxc chmod +x lyxc # In PATH verschieben (systemweit) sudo mv lyxc /usr/local/bin/ # Installation prüfen lyxc --version Erwartete Ausgabe: lyxc version: 0.8.5-aerospace Target: x86_64-linux-elf Reproducible: yes ==== Windows ==== # PowerShell (als Administrator) Invoke-WebRequest https://seolizer.de/lyx/releases/latest/lyxc-windows-x86_64.exe -OutFile lyxc.exe # In PATH eintragen oder direkt aufrufen: .\lyxc.exe --version ==== Unterstützte Zielplattformen ==== ^ Plattform ^ Flag ^ | Linux x86_64 | ''--target=x86_64'' (Default) | | Linux ARM64 | ''--target=arm64'' | | macOS x86_64 | ''--target=macos-x86_64'' | | macOS Apple Silicon | ''--target=macos-arm64'' | | RISC-V 64 | ''--target=riscv64'' | | ESP32 (Xtensa) | ''--target=xtensa'' | Der Compiler läuft immer auf dem Host-System — Cross-Compilation auf eine andere Zielarchitektur ist über ''--target'' möglich, ohne weitere Tools. ---- ===== 2. Hello World ===== Erstelle eine Datei ''hello.lyx'': fn main(): int64 { PrintStr("Hello Lyx!\n"); return 0; } Kompilieren und ausführen: lyxc hello.lyx -o hello ./hello Hello Lyx! Was hier passiert: * Jedes Lyx-Programm braucht eine Funktion ''main'', die ''int64'' zurückgibt. Der Rückgabewert ist der Exit-Code des Prozesses — ''0'' bedeutet Erfolg. * ''PrintStr'' ist eine eingebaute Ausgabefunktion. Sie erwartet einen nullterminierten String. * Kein ''import'' nötig für die Basis-Ausgabefunktionen — sie sind Teil der impliziten Laufzeitumgebung. ---- ===== 3. Units und Imports ===== Größere Programme bestehen aus mehreren Dateien. Jede Datei beginnt mit einer **Unit-Deklaration** — dem Namen des Moduls. Andere Units werden mit ''import'' eingebunden. unit Greeter; // Name dieser Unit import std.io; // Standard-Ausgabe und Eingabe import std.math; // Mathematische Funktionen fn main(): int64 { PrintStr("Pi ist ungefähr "); PrintF64(math.PI); PrintStr("\n"); return 0; } lyxc greeter.lyx -o greeter ./greeter Pi ist ungefähr 3.141593 Ohne ''unit''-Deklaration behandelt der Compiler die Datei als anonyme Top-Level-Unit. Für einzelne Programmdateien ist das in Ordnung — für wiederverwendbare Bibliotheken sollte immer ein Name angegeben werden. ---- ===== 4. Variablen und Speicherklassen ===== Lyx kennt vier Speicherklassen. Die Wahl der richtigen Speicherklasse ist keine Stilfrage — sie ist Dokumentation der Absicht und wird vom Compiler überprüft. ^ Schlüsselwort ^ Bedeutung ^ Änderbar? ^ | ''var'' | Veränderliche Variable | Ja | | ''let'' | Einmalig zugewiesener Wert | Nein (nach erstem Wert) | | ''co'' | Konfigurationswert (Runtime-Konstante) | Nein | | ''con'' | Compile-Zeit-Konstante | Nein — Wert muss zur Compile-Zeit bekannt sein | fn main(): int64 { var counter: int64 := 0; // Zähler — wird verändert let name: int64 := "Lyx"; // Einmalig gesetzt, danach unveränderlich co max_retries: int64 := 5; // Konfigurationswert — unveränderlich zur Laufzeit con BUFFER_SIZE: int64 := 4096; // Compile-Zeit-Konstante — kein Speicher zur Laufzeit counter := counter + 1; // OK // name := "andere"; // Compiler-Fehler: let-Variable ist unveränderlich PrintInt(counter); PrintStr("\n"); return 0; } Der häufigste Fehler für Einsteiger: '':='' ist Zuweisung, ''='' ist Vergleich. var x: int64 := 10; // Zuweisung: x bekommt den Wert 10 if (x = 10) { ... } // Vergleich: ist x gleich 10? x := 20; // Zuweisung: x bekommt den Wert 20 ---- ===== 5. Grundlegende Datentypen ===== Lyx ist streng typisiert — es gibt keine impliziten Konvertierungen. Jeder Typ muss explizit angegeben oder eindeutig inferiert werden. ^ Typ ^ Beschreibung ^ Beispiel ^ | ''int64'' | 64-Bit-Ganzzahl (signed) | ''var x: int64 := 42'' | | ''int32'' | 32-Bit-Ganzzahl (signed) | ''var x: int32 := 42i32'' | | ''uint8'' | 8-Bit vorzeichenlos (Byte) | ''var b: uint8 := 0xFFu8'' | | ''f64'' | 64-Bit-Fließkommazahl | ''var pi: f64 := 3.14'' | | ''f32'' | 32-Bit-Fließkommazahl | ''var x: f32 := 1.0f32'' | | ''bool'' | Wahrheitswert | ''var ok: bool := true'' | | ''pchar'' | Zeiger auf nullterminierten String | ''var s: pchar := "Hallo"'' | Explizite Typkonvertierung mit ''as'': fn main(): int64 { var ganzzahl: int64 := 7; var kommazahl: f64 := ganzzahl as f64; // int64 → f64 var gerundet: int64 := kommazahl as int64; // f64 → int64 (abgeschnitten) var byte_wert: uint8 := 200u8; var als_int: int64 := byte_wert as int64; // uint8 → int64 PrintF64(kommazahl); PrintStr("\n"); return 0; } ---- ===== 6. Funktionen ===== Funktionen werden mit ''fn'' deklariert. Der Rückgabetyp folgt nach dem Doppelpunkt hinter der Parameterliste. // Einfache Funktion mit zwei Parametern fn Add(a: int64, b: int64): int64 { return a + b; } // Funktion ohne Rückgabewert fn PrintLine(msg: int64): void { PrintStr(msg); PrintStr("\n"); } // Funktion mit mehreren Rückgabewerten (Tuple) fn Divide(a: int64, b: int64): (int64, bool) { if (b == 0) { return (0, false); // Fehlerfall } return (a / b, true); // Ergebnis und Erfolg } fn main(): int64 { var sum: int64 := Add(3, 4); PrintLine("Summe berechnet"); var (result, ok): (int64, bool) := Divide(10, 3); if (ok) { PrintStr("Ergebnis: "); PrintInt(result); PrintStr("\n"); } return 0; } Tuple-Rückgaben sind ein direkter Weg, mehrere Werte zurückzugeben — ohne Zeiger, ohne Out-Parameter, ohne Fehler-Exceptions. ---- ===== 7. Kontrollfluss ===== ==== if / else ==== fn Classify(n: int64): void { if (n < 0) { PrintStr("negativ\n"); } else if (n == 0) { PrintStr("null\n"); } else { PrintStr("positiv\n"); } } ==== while-Schleife ==== Die Standard-Schleife in Lyx. Für sicherheitskritischen Code kann mit ''limit(N)'' ein hartes Maximum an Iterationen gesetzt werden — der Compiler kann dann die Endlichkeit statisch nachweisen. fn main(): int64 { var i: int64 := 0; // Normale while-Schleife while (i < 5) { PrintInt(i); PrintStr("\n"); i := i + 1; } // Schleife mit Iterations-Limit (für Safety-Code) var j: int64 := 0; while (j < 100) limit(100) { j := j + 1; } return 0; } ==== Pattern Matching ==== ''match'' wertet einen Ausdruck gegen Muster aus und ist erschöpfend — der Compiler prüft, ob alle möglichen Fälle abgedeckt sind. fn DayName(day: int64): int64 { match day { 1 => return "Montag"; 2 => return "Dienstag"; 3 => return "Mittwoch"; 4 => return "Donnerstag"; 5 => return "Freitag"; 6 => return "Samstag"; 7 => return "Sonntag"; _ => return "Unbekannt"; } } fn main(): int64 { PrintStr(DayName(3)); PrintStr("\n"); return 0; } ---- ===== 8. Arrays ===== Arrays haben eine feste Größe, die zur Compile-Zeit bekannt sein muss. Sie liegen auf dem Stack — kein Heap, keine versteckte Allokation. fn main(): int64 { var temperatures: f64[5] := [20.1, 21.3, 19.8, 22.0, 20.5]; // Elemente ausgeben var i: int64 := 0; while (i < 5) limit(5) { PrintStr("Messung "); PrintInt(i + 1); PrintStr(": "); PrintF64(temperatures[i]); PrintStr(" °C\n"); i := i + 1; } // Summe berechnen var sum: f64 := 0.0; i := 0; while (i < 5) limit(5) { sum := sum + temperatures[i]; i := i + 1; } PrintStr("Durchschnitt: "); PrintF64(sum / 5.0); PrintStr(" °C\n"); return 0; } ---- ===== 9. Structs ===== Structs fassen zusammengehörige Daten unter einem gemeinsamen Namen zusammen. type Point = struct { x: f64; y: f64; }; fn Distance(a: Point, b: Point): f64 { var dx: f64 := b.x - a.x; var dy: f64 := b.y - a.y; return math.Sqrt(dx * dx + dy * dy); } fn main(): int64 { var p1: Point := Point { x: 0.0, y: 0.0 }; var p2: Point := Point { x: 3.0, y: 4.0 }; PrintStr("Abstand: "); PrintF64(Distance(p1, p2)); PrintStr("\n"); // Ausgabe: 5.0 return 0; } ---- ===== 10. Der Pipe-Operator ===== ''|>'' leitet den Ergebniswert eines Ausdrucks als erstes Argument an die nächste Funktion weiter. Das macht Datenpipelines lesbar — von links nach rechts, ohne verschachtelte Funktionsaufrufe. import std.math; import std.string; fn Double(x: f64): f64 { return x * 2.0; } fn Square(x: f64): f64 { return x * x; } fn Negate(x: f64): f64 { return -x; } fn main(): int64 { // Ohne Pipe: schwer zu lesen var result1: f64 := Negate(Square(Double(3.0))); // Mit Pipe: von links nach rechts var result2: f64 := 3.0 |> Double() |> Square() |> Negate(); // 3.0 → 6.0 → 36.0 → -36.0 PrintF64(result2); PrintStr("\n"); return 0; } ---- ===== 11. Speicherverwaltung ===== Lyx hat keinen Garbage Collector. Speicher auf dem Heap wird mit ''new'' angefordert und muss mit ''dispose'' freigegeben werden. Für die meisten Anwendungsfälle reicht der Stack aus. fn main(): int64 { // Stack — automatisch, kein Aufräumen nötig var stack_array: int64[256]; // 256 × 8 Byte auf dem Stack // Heap — manuell verwaltet var heap_ptr: int64 := new int64[1000]; // 1000 × 8 Byte auf dem Heap // ... Arbeit mit heap_ptr ... dispose heap_ptr; // Freigabe — sonst Memory Leak return 0; } Faustregel: Wenn die Größe zur Compile-Zeit bekannt ist und unter ~100 KB liegt — Stack. Wenn dynamisch oder groß — Heap mit ''new'' + ''dispose''. ---- ===== 12. Wichtige Compiler-Flags ===== ^ Flag ^ Wirkung ^ | ''lyxc datei.lyx -o programm'' | Kompiliert und linkt zu einem Binary | | ''lyxc datei.lyx --lint'' | Prüft auf Stil- und Safety-Verstöße | | ''lyxc datei.lyx --lint-only'' | Nur Lint, kein Binary | | ''lyxc datei.lyx --static-analysis'' | Tiefe statische Analyse | | ''lyxc datei.lyx --target=arm64'' | Cross-Compilation für ARM64 | | ''lyxc datei.lyx --compile-unit -o datei.lyu'' | Vorkompilierte Unit erzeugen | | ''lyxc datei.lyx -I ./build/'' | Suchpfad für vorkompilierte Units (.lyu) | | ''lyxc datei.lyx --emit-asm'' | Assembler-Ausgabe (zur Analyse) | | ''lyxc --version'' | Compiler-Version anzeigen | | ''lyxc --build-info'' | Detaillierte Build-Konfiguration | ---- ===== 13. Vollständiges Beispiel: Temperatur-Logger ===== Dieses Beispiel kombiniert alle Grundkonzepte: Unit-Deklaration, Struct, Funktionen, Schleife, Ausgabe. unit TempLogger; import std.io; // Konfiguration — Compile-Zeit-Konstanten con MAX_READINGS: int64 := 10; con ALARM_TEMP: f64 := 85.0; // Datenstruktur für eine Messung type Reading = struct { sensor_id: int64; temperature: f64; alarm: bool; }; // Erstellt eine Messung und setzt den Alarm-Status fn MakeReading(id: int64, temp: f64): Reading { return Reading { sensor_id: id, temperature: temp, alarm: temp >= ALARM_TEMP }; } // Gibt eine Messung formatiert aus fn PrintReading(r: Reading): void { PrintStr("Sensor "); PrintInt(r.sensor_id); PrintStr(": "); PrintF64(r.temperature); PrintStr(" °C"); if (r.alarm) { PrintStr(" *** ALARM ***"); } PrintStr("\n"); } fn main(): int64 { // Simulierte Messwerte var temps: f64[10] := [ 72.3, 75.1, 79.8, 83.2, 86.0, 88.5, 84.1, 80.3, 77.9, 74.2 ]; var alarm_count: int64 := 0; var i: int64 := 0; while (i < MAX_READINGS) limit(MAX_READINGS) { var r: Reading := MakeReading(i + 1, temps[i]); PrintReading(r); if (r.alarm) { alarm_count := alarm_count + 1; } i := i + 1; } PrintStr("\nAlarm-Ereignisse: "); PrintInt(alarm_count); PrintStr(" von "); PrintInt(MAX_READINGS); PrintStr(" Messungen\n"); return 0; } lyxc temp_logger.lyx -o temp_logger ./temp_logger Sensor 1: 72.300000 °C Sensor 2: 75.100000 °C Sensor 3: 79.800000 °C Sensor 4: 83.200000 °C Sensor 5: 86.000000 °C *** ALARM *** Sensor 6: 88.500000 °C *** ALARM *** Sensor 7: 84.100000 °C Sensor 8: 80.300000 °C Sensor 9: 77.900000 °C Sensor 10: 74.200000 °C Alarm-Ereignisse: 2 von 10 Messungen ---- ===== Nächste Schritte ===== ^ Thema ^ Seite ^ | Alle Sprachkonstrukte im Detail | [[lyx_-_programmiersprache:syntax|Syntax-Referenz]] | | Datentypen vollständig | [[lyx_-_programmiersprache:datentypen|Datentypen]] | | Kontrollfluss und Schleifen | [[lyx_-_programmiersprache:schleifen|Schleifen]] | | Objektorientierung | [[lyx_-_programmiersprache:oop|OOP – Klassen & Vererbung]] | | Generics & Traits | [[lyx_-_programmiersprache:generics-traits|Generics & Traits]] | | Speicherverwaltung | [[lyx_-_programmiersprache:memory-management|Memory Management]] | | Standardbibliothek | [[lyx_-_programmiersprache:units|Standardbibliothek – Übersicht]] | | Safety-Entwicklung | [[lyx_-_programmiersprache:aerospace-safety|Aerospace & Safety]] | | Aerospace-Tutorial | [[lyx_-_programmiersprache:aerospace-tutorial|Von der Anforderung zum Nachweis]] |