====== 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.9.3B 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 { PrintLn("Hello Lyx!"); 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. * ''PrintLn'' gibt einen String gefolgt von einem Zeilenumbruch aus. ''Print'' gibt ohne Zeilenumbruch aus. Beide erwarten 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 { Print("Pi ist ungefähr "); PrintF64(math.PI); PrintLn(""); 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 PrintLn(IntToStr(counter)); 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); PrintLn(""); 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: pchar) { PrintLn(msg); } // Funktion mit mehreren Rückgabewerten (Tupel) 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) { PrintLn("Ergebnis: " + IntToStr(result)); } 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) { PrintLn("negativ"); } else if (n = 0) { PrintLn("null"); } else { PrintLn("positiv"); } } ==== 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) { PrintLn(IntToStr(i)); 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): pchar { match (day) { case 1 => return "Montag"; case 2 => return "Dienstag"; case 3 => return "Mittwoch"; case 4 => return "Donnerstag"; case 5 => return "Freitag"; case 6 => return "Samstag"; case 7 => return "Sonntag"; default => return "Unbekannt"; } } fn main(): int64 { PrintLn(DayName(3)); 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: [5]f64 := [20.1, 21.3, 19.8, 22.0, 20.5]; // Elemente ausgeben var i: int64 := 0; while (i < 5) limit(5) { Print("Messung " + IntToStr(i + 1) + ": "); PrintF64(temperatures[i]); PrintLn(" °C"); i := i + 1; } // Summe berechnen var sum: f64 := 0.0; i := 0; while (i < 5) limit(5) { sum := sum + temperatures[i]; i := i + 1; } Print("Durchschnitt: "); PrintF64(sum / 5.0); PrintLn(" °C"); 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 }; Print("Abstand: "); PrintF64(Distance(p1, p2)); PrintLn(""); // 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); PrintLn(""); 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: [256]int64 := []; // 256 × 8 Byte auf dem Stack // Heap — dynamisches Array (wächst zur Laufzeit) var heap_list: array := []; heap_list.push(42); // ... Arbeit mit heap_list ... dispose heap_list; // Freigabe — sonst Memory Leak return 0; } Faustregel: Wenn die Größe zur Compile-Zeit bekannt ist und unter ~100 KB liegt — Stack (''[N]T''). Wenn dynamisch — Heap mit ''array'' + ''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 { Print("Sensor " + IntToStr(r.sensor_id) + ": "); PrintF64(r.temperature); Print(" °C"); if (r.alarm) { Print(" *** ALARM ***"); } PrintLn(""); } fn main(): int64 { // Simulierte Messwerte var temps: [10]f64 := [ 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; } PrintLn(""); PrintLn("Alarm-Ereignisse: " + IntToStr(alarm_count) + " von " + IntToStr(MAX_READINGS) + " Messungen"); 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:sprache:syntax|Syntax-Referenz]] | | Datentypen vollständig | [[lyx_-_programmiersprache:sprache:datentypen|Datentypen]] | | Kontrollfluss und Schleifen | [[lyx_-_programmiersprache:sprache:schleifen|Schleifen]] | | Objektorientierung | [[lyx_-_programmiersprache:sprache:oop|OOP – Klassen & Vererbung]] | | Generics & Traits | [[lyx_-_programmiersprache:sprache:generics-traits|Generics & Traits]] | | Speicherverwaltung | [[lyx_-_programmiersprache:sprache:memory-management|Memory Management]] | | Rohspeicher (alloc/peek/poke) | [[lyx_-_programmiersprache:sprache:rohspeicher|Rohspeicher]] | | Handles und Dateideskriptoren | [[lyx_-_programmiersprache:sprache:handles-und-fd|Handles und Dateideskriptoren]] | | Fehlerkonventionen der Stdlib | [[lyx_-_programmiersprache:sprache:std-fehlerkonventionen|Std-Fehlerkonventionen]] | | Standardbibliothek | [[lyx_-_programmiersprache:units|Standardbibliothek – Übersicht]] | | Safety-Entwicklung | [[lyx_-_programmiersprache:guides:aerospace-safety|Aerospace & Safety]] | | Aerospace-Tutorial | [[lyx_-_programmiersprache:guides:aerospace-tutorial|Von der Anforderung zum Nachweis]] |