====== 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]] |