====== 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.
Alle Beispiele dieser Seite sind mit ''lyxc 1.0.21A'' übersetzt und ausgeführt; die gezeigten Ausgaben sind die echten Programmausgaben.
----
===== 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 1.0.17K
Build: bootstrap
Ausführlicher meldet sich ''lyxc --build-info'' — Name, Version, Build-Art und Zielplattform.
==== 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 ====
Der Compiler läuft immer auf dem Host-System; ''--target'' bestimmt, wofür er erzeugt. Cross-Compilation braucht keine weiteren Werkzeuge.
^ Plattform ^ Flag ^ Ergebnis ^
| Linux x86_64 | ''--target=linux'' (Standard) | ELF 64-bit, x86-64 |
| Linux ARM64 | ''--target=arm64'' | ELF 64-bit, AArch64 |
| macOS x86_64 | ''--target=macosx64'' | Mach-O 64-bit x86_64 |
| macOS Apple Silicon | ''--target=macos-arm64'' | Mach-O 64-bit arm64 |
| Windows x86_64 | ''--target=win64'' | PE32+ (Konsole) |
| RISC-V 64 | ''--target=riscv'' | ELF 64-bit, RISC-V |
| ESP32 (Xtensa) | ''--target=esp32'' | ELF 32-bit, Xtensa |
| Android | ''--target=android-arm64'' / ''--target=android-x86_64'' | ELF 64-bit |
> **''%%--%%target'' und ''%%--%%arch'' sind zweierlei.** ''xtensa'', ''riscv'' und ''x86_64'' sind Werte für ''%%--%%arch''. Wer ''%%--%%target=xtensa'' schreibt, bekommt **kommentarlos ein x86-64-Binary** — für den ESP32 lautet das Ziel ''esp32''. Geprüft mit 1.0.21A.
----
===== 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''. Gibt sie ''int64'' zurück, **ist dieser Rückgabewert der Exit-Code des Prozesses** — ''0'' bedeutet Erfolg, alles andere einen Fehler. Der ''_start''-Stub reicht den Wert unverändert an ''exit_group'' weiter; unter Linux kommen davon nur die unteren 8 Bit an (''%%return 256%%'' ergibt also ''0''). Details: [[lyx_-_programmiersprache:sprache:exit-codes|Exit-Codes]].
* ''PrintLn'' gibt einen Text mit Zeilenumbruch aus, ''Print'' ohne. Beide erwarten einen nullterminierten Text (''pchar'').
* Für die Basis-Ausgabe braucht es **kein ''import''** — ''PrintLn'', ''Print'', ''PrintF64'' und einige andere sind Builtins. Sobald mehr dazukommt (''IntToStr'', ''StrConcat'', ''FloatToStr''), werden ''std.io'' und ''std.string'' gebraucht.
----
===== 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; // Ein-/Ausgabe
import std.math.constants; // PI, E, …
fn main(): int64 {
Print("Pi ist ungefähr ");
PrintF64(PI);
return 0;
}
lyxc greeter.lyx -o greeter
./greeter
Pi ist ungefähr 3.141592
> **Es gibt keinen qualifizierten Zugriff.** ''math.PI'' oder ''std.math.PI'' sind keine gültige Schreibweise — der Compiler meldet ''undefined symbol 'math'''. Alle importierten Symbole liegen in **einem flachen Namensraum** und werden unqualifiziert angesprochen: ''PI'', ''SqrtF64'', ''StrConcat''.
>
> Deshalb tragen die Units Namenspräfixe (''Str*'', ''SQLite*'', ''Pdf*''), und deshalb kollidieren manche Units miteinander — etwa ''std.math'' und ''std.ml'' (beide exportieren ''SqrtF64''). Ein Import zu viel bricht die Übersetzung ab, bevor das Symbol überhaupt benutzt wird.
Ein Punktpfad bezeichnet immer genau eine Datei: ''std.math.constants'' → ''std/math/constants.lyx''. Wildcards (''%%import std.audio.*;%%'') und Verzeichnis-Importe (''import std.net;'') gibt es nicht.
Ohne ''unit''-Zeile behandelt der Compiler die Datei als anonyme Top-Level-Unit — für ein einzelnes Programm in Ordnung, für alles Wiederverwendbare sollte ein Name stehen.
----
===== 4. Variablen und Speicherklassen =====
Lyx kennt vier Speicherklassen. Die Wahl ist keine Stilfrage — sie dokumentiert die Absicht und wird vom Compiler geprüft.
^ Schlüsselwort ^ Bedeutung ^ Änderbar? ^
| ''var'' | Veränderliche Variable | Ja |
| ''let'' | Einmalig zugewiesener Wert | Nein (nach erstem Wert) |
| ''co'' | Konstante zur Laufzeit (Stack) | Nein |
| ''con'' | Compile-Zeit-Konstante | Nein — Wert muss zur Übersetzungszeit feststehen |
unit main;
import std.io;
import std.string;
con BUFFER_SIZE: int64 := 4096; // Compile-Zeit-Konstante
fn main(): int64 {
var counter: int64 := 0; // wird verändert
let name: pchar := "Lyx"; // einmalig gesetzt
co max_retries: int64 := 5; // zur Laufzeit unveränderlich
counter := counter + 1;
// name := "andere"; // Fehler: let ist unveränderlich
PrintLn(name, ": ", IntToStr(counter));
PrintLn("Puffer: ", IntToStr(BUFFER_SIZE));
PrintLn("Versuche: ", IntToStr(max_retries));
return 0;
}
Lyx: 1
Puffer: 4096
Versuche: 5
Der häufigste Anfängerfehler: '':='' ist Zuweisung, ''=='' ist Vergleich. In einer Bedingung wird ein einzelnes ''='' abgewiesen — ''%%if (x = 10)%%'' ist ein Parse-Fehler. (Als eigenständige Anweisung übersetzt ''%%x = 10;%%'' derzeit noch als Zuweisung, [[https://github.com/SEOLizer/LyX-Compiler/issues/1575|#1575]] — gewöhne dir '':='' trotzdem konsequent an.)
var x: int64 := 10; // Zuweisung
if (x == 10) { } // Vergleich
x := 20; // Zuweisung
----
===== 5. Grundlegende Datentypen =====
Lyx ist streng typisiert — es gibt keine impliziten Konvertierungen. Jeder Typ muss angegeben oder eindeutig ableitbar sein.
^ Typ ^ Kurzform ^ Beschreibung ^ Beispiel ^
| ''int64'' | ''i64'' | 64-Bit-Ganzzahl mit Vorzeichen | ''%%var x: int64 := 42;%%'' |
| ''int32'' | ''i32'' | 32-Bit-Ganzzahl mit Vorzeichen | ''%%var x: int32 := 42;%%'' |
| ''uint8'' | ''u8'' | 8 Bit ohne Vorzeichen (Byte) | ''%%var b: uint8 := 200;%%'' |
| ''f64'' | – | 64-Bit-Fließkommazahl | ''%%var pi: f64 := 3.14;%%'' |
| ''f32'' | – | 32-Bit-Fließkommazahl | ''%%var x: f32 := 1.0;%%'' |
| ''bool'' | – | Wahrheitswert | ''%%var ok: bool := true;%%'' |
| ''pchar'' | – | nullterminierter Text | ''%%var s: pchar := "Hallo";%%'' |
Ein blankes ''int'' gibt es **nicht** — die Breite gehört zum Namen. Vollständig: [[lyx_-_programmiersprache:sprache:datentypen|Datentypen]].
Explizite Umwandlung mit ''as'':
unit main;
import std.io;
fn main(): int64 {
var ganzzahl: int64 := 7;
var kommazahl: f64 := ganzzahl as f64; // int64 → f64
var gerundet: int64 := kommazahl as int64; // f64 → int64 (schneidet ab)
var byte_wert: uint8 := 200;
var als_int: int64 := byte_wert as int64; // uint8 → int64
PrintF64(kommazahl);
PrintLn(IntToStr(gerundet));
PrintLn(IntToStr(als_int));
return 0;
}
7.000000
7
200
----
===== 6. Funktionen =====
Funktionen werden mit ''fn'' deklariert; der Rückgabetyp folgt nach dem Doppelpunkt hinter der Parameterliste.
unit main;
import std.io;
import std.string;
// Zwei Parameter, ein Rückgabewert
fn Add(a: int64, b: int64): int64 {
return a + b;
}
// Ohne Rückgabewert
fn PrintLine(msg: pchar): void {
PrintLn(msg);
}
// Mehrere Rückgabewerte als Tupel
fn Divide(a: int64, b: int64): (int64, bool) {
if (b == 0) { return (0, false); }
return (a / b, true);
}
fn main(): int64 {
var sum: int64 := Add(3, 4);
PrintLine(StrConcat("Summe: ", IntToStr(sum)));
var result, ok := Divide(10, 3);
if (ok) {
PrintLn("Ergebnis: ", IntToStr(result));
}
return 0;
}
Summe: 7
Ergebnis: 3
Tupel-Rückgaben sind der direkte Weg für „Wert **und** Erfolg" — ohne Zeiger, ohne Out-Parameter, ohne Ausnahmen. Genau zwei Elemente sind möglich, weil die Aufrufkonvention zwei Register trägt.
----
===== 7. Kontrollfluss =====
==== if / else ====
fn Classify(n: int64): void {
if (n < 0) {
PrintLn("negativ");
} else if (n == 0) {
PrintLn("null");
} else {
PrintLn("positiv");
}
}
Die Klammern um die Bedingung und die geschweiften Klammern sind **Pflicht**, auch bei einer einzigen Anweisung.
==== Schleifen ====
Es gibt ''while'', ''for … to''/''downto'' und ''repeat … until''. Mit ''limit(N)'' bekommt eine Schleife eine harte Obergrenze — der Compiler kann damit beweisen, dass sie endet. In ''@flight_crit''- und ''@dal(A/B)''-Modulen ist das Pflicht.
fn main(): int64 {
var i: int64 := 0;
while (i < 5) {
PrintLn(IntToStr(i));
i := i + 1;
}
for j := 0 to 4 do { // beide Grenzen einschließlich
PrintLn(IntToStr(j));
}
var k: int64 := 0;
while (k < 100) limit(100) { // nachweisbar endlich
k := k + 1;
}
return 0;
}
==== Pattern Matching ====
''match'' prüft einen Wert gegen Muster und ist erschöpfend — fehlt ein Fall, meldet es der Compiler.
unit main;
import std.io;
fn DayName(day: int64): pchar {
match (day) {
case 1 => { return "Montag"; }
case 2 => { return "Dienstag"; }
case 3 => { return "Mittwoch"; }
case 6 | 7 => { return "Wochenende"; }
case _ => { return "Unbekannt"; }
}
}
fn main(): int64 {
PrintLn(DayName(3));
return 0;
}
Mittwoch
----
===== 8. Arrays =====
Feste Arrays haben eine zur Übersetzungszeit bekannte Größe und liegen auf dem Stack — keine Allokation, kein Aufräumen.
unit main;
import std.io;
import std.string;
fn main(): int64 {
var temperatures: [5]f64 := [20.1, 21.3, 19.8, 22.0, 20.5];
var i: int64 := 0;
while (i < 5) limit(5) {
Print("Messung ", IntToStr(i + 1), ": ");
PrintLn(FloatToStr(temperatures[i], 1));
i := i + 1;
}
var sum: f64 := 0.0;
i := 0;
while (i < 5) limit(5) {
var wert: f64 := temperatures[i]; // erst kopieren, dann rechnen
sum := sum + wert;
i := i + 1;
}
Print("Durchschnitt: ");
PrintLn(FloatToStr(sum / 5.0, 2));
return 0;
}
Messung 1: 20.0
Messung 2: 21.2
Messung 3: 19.7
Messung 4: 22.0
Messung 5: 20.5
Durchschnitt: 20.73
> **''f64''-Array-Elemente rechnen seit lyxc 1.0.17K richtig** (#1373). ''%%sum := sum + t[i];%%'' und ''%%t[0] + 1.0%%'' liefern den Wert, nicht mehr das Bitmuster — der Umweg über eine lokale Variable ist nicht mehr nötig (er schadet aber auch nicht). Bis 1.0.17I wurde aus der Summe von ''[20.1, 21.3, 19.8, 22.0, 20.5]'' noch ''4626531008699107328'' statt ''103.7''.
Auch zu beachten: ''FloatToStr'' rundet **ab** statt kaufmännisch — ''20.1'' mit einer Nachkommastelle wird zu ''20.0''. Für exakte Ausgabe eine Stelle mehr wählen.
----
===== 9. Structs =====
Structs fassen zusammengehörige Daten zusammen. Sie liegen auf dem Stack und werden bei der Zuweisung kopiert.
unit main;
import std.io;
import std.math;
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 SqrtF64(dx * dx + dy * dy);
}
fn main(): int64 {
var p1: Point;
p1.x := 0.0;
p1.y := 0.0;
var p2: Point;
p2.x := 3.0;
p2.y := 4.0;
Print("Abstand: ");
PrintF64(Distance(p1, p2));
return 0;
}
Abstand: 5.000000
Die Felder werden **einzeln zugewiesen**; eine Literal-Schreibweise wie ''%%Point { x: 3.0, y: 4.0 }%%'' kennt der Parser nicht. Die Wurzelfunktion heißt ''SqrtF64'', nicht ''Sqrt'' — die Fließkomma-Funktionen aus ''std.math'' tragen durchgehend das Suffix ''F64''.
----
===== 10. Der Pipe-Operator =====
''%%|>%%'' reicht den linken Wert als **erstes Argument** an die nächste Funktion weiter. Das macht Verarbeitungsketten von links nach rechts lesbar.
unit main;
import std.io;
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: von innen nach außen 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);
return 0;
}
-36.000000
Weitere Argumente sind erlaubt — ''%%50 |> Clamp(0, 30)%%'' bedeutet ''Clamp(50, 0, 30)''. Soll der Wert an eine andere Stelle, markiert ihn ein ''?'': ''%%50 |> Clamp(0, ?, 30)%%''.
----
===== 11. Speicherverwaltung =====
Lyx hat keinen Garbage Collector. Der Stack reicht für die meisten Fälle; Heap-Speicher wird ausdrücklich angefordert und wieder freigegeben.
unit main;
import std.io;
import std.string;
fn main(): int64 {
// Stack — automatisch, kein Aufräumen
var werte: [256]int64;
// Heap — dynamisches Array
var liste: array := [];
liste.push(42);
liste.push(17);
PrintLn("Einträge: ", IntToStr(len(liste)));
dispose liste; // Freigabe
return 0;
}
Einträge: 2
Faustregel: Größe zur Übersetzungszeit bekannt und unter etwa 100 KB → Stack (''[N]T''). Dynamisch → ''array<T>'' oder ''%%new T[n]%%'' mit ''dispose''. Rohe Puffer holt man mit ''alloc(n)'' und gibt sie mit ''free(ptr, size)'' zurück → [[lyx_-_programmiersprache:sprache:memory-management|Memory Management]].
----
===== 12. Wichtige Compiler-Flags =====
^ Flag ^ Wirkung ^
| ''lyxc datei.lyx -o programm'' | Übersetzen und linken in einem Schritt |
| ''lyxc datei.lyx -I ./src'' | Suchpfad für eigene Units |
| ''lyxc datei.lyx --runtime-checks'' | Bereichs- und Nullprüfungen zur Laufzeit einschalten |
| ''lyxc datei.lyx --lint'' | Stil- und Safety-Warnungen |
| ''lyxc datei.lyx --lint-only'' | Nur prüfen, kein Binary |
| ''lyxc datei.lyx --static-analysis'' | Datenfluss, lebende Variablen, Stack |
| ''lyxc datei.lyx --call-graph'' | Aufrufgraph, Rekursionserkennung |
| ''lyxc datei.lyx --target=arm64'' | Für ARM64 übersetzen |
| ''lyxc datei.lyx --compile-unit -o datei.lyu'' | Unit vorkompilieren (''-o'' ist Pflicht) |
| ''lyxc --version'' / ''--build-info'' | Version und Build-Konfiguration |
**Ohne ''%%--%%runtime-checks'' wird ein Zugriff außerhalb der Array-Grenzen nicht bemerkt** — der Zugriff liest dann kommentarlos fremden Speicher. Vollständig: [[lyx_-_programmiersprache:tools:compiler-parameter|Compiler-Parameter]].
----
===== 13. Vollständiges Beispiel: Temperatur-Logger =====
Dieses Beispiel verbindet die Grundkonzepte: Unit, Konstante, Struct, Funktionen, Schleife mit ''limit'', Ausgabe.
unit main;
import std.io;
import std.string;
con MAX_READINGS: int64 := 10;
type Reading = struct {
sensor_id: int64;
temperature: f64;
alarm: bool;
};
fn MakeReading(id: int64, temp: f64): Reading {
var alarm_temp: f64 := 85.0; // lokal halten — siehe Kasten unten
var r: Reading;
r.sensor_id := id;
r.temperature := temp;
r.alarm := temp >= alarm_temp;
return r;
}
fn PrintReading(r: Reading): void {
Print("Sensor ", IntToStr(r.sensor_id), ": ");
Print(FloatToStr(r.temperature, 1));
Print(" Grad");
if (r.alarm) { Print(" *** ALARM ***"); }
PrintLn("");
}
fn main(): int64 {
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 10 Messungen");
return 0;
}
lyxc temp_logger.lyx -o temp_logger
./temp_logger
Sensor 1: 72.2 Grad
Sensor 2: 75.0 Grad
Sensor 3: 79.7 Grad
Sensor 4: 83.1 Grad
Sensor 5: 86.0 Grad *** ALARM ***
Sensor 6: 88.5 Grad *** ALARM ***
Sensor 7: 84.0 Grad
Sensor 8: 80.2 Grad
Sensor 9: 77.8 Grad
Sensor 10: 74.1 Grad
Alarm-Ereignisse: 2 von 10 Messungen
> **Fließkomma-Schwellwerte nicht als globales ''con'' ablegen.** Globale ''var''- und ''co''-Variablen vom Typ ''f64'' rechnen seit lyxc 1.0.17K richtig; ''con'' nicht. Mit ''%%con ALARM_TEMP: f64 := 85.0;%%'' ist ''%%86.0 >= ALARM_TEMP%%'' **falsch**, und ''%%ALARM_TEMP + 0.0%%'' ergibt ''4635681760191971328'' — die Konstante kommt als Bitmuster in die Rechnung. Die direkte Ausgabe mit ''PrintF64'' stimmt dagegen, der Fehler fällt deshalb leicht nicht auf.
>
> Bis das behoben ist: den Schwellwert **lokal** anlegen (wie oben), als globales ''var''/''co'' schreiben oder die Konstante zuerst in eine lokale Variable kopieren. Ganzzahlige Globals (''%%con MAX_READINGS: int64%%'') sind nicht betroffen. ([[https://github.com/SEOLizer/LyX-Compiler/issues/1373|Issue #1373]], geprüft mit 1.0.17K)
----
===== Nächste Schritte =====
^ Thema ^ Seite ^
| Die Sprache im Überblick, mit Lesepfaden | [[lyx_-_programmiersprache:sprache|Sprachreferenz]] |
| Alle Sprachkonstrukte im Detail | [[lyx_-_programmiersprache:sprache:syntax|Syntax-Referenz]] |
| Variablen: Speicherklassen, Gültigkeit, Lebensdauer | [[lyx_-_programmiersprache:sprache:variablen|Variablen]] |
| Datentypen vollständig | [[lyx_-_programmiersprache:sprache:datentypen|Datentypen]] |
| Kontrollfluss und Schleifen | [[lyx_-_programmiersprache:sprache:schleifen|Schleifen]] |
| Objektorientierung | [[lyx_-_programmiersprache:sprache:oop|OOP – Klassen & Vererbung]] |
| Interfaces und generische Funktionen | [[lyx_-_programmiersprache:sprache:generics-traits|Generics & Interfaces]] |
| Speicherverwaltung | [[lyx_-_programmiersprache:sprache:memory-management|Memory Management]] |
| Rohspeicher (alloc/peek/poke) | [[lyx_-_programmiersprache:sprache:rohspeicher|Rohspeicher]] |
| Fehlerkonventionen der Stdlib | [[lyx_-_programmiersprache:sprache:std-fehlerkonventionen|Std-Fehlerkonventionen]] |
| Die passende Unit finden | [[lyx_-_programmiersprache:guides:welche-unit|Welche Unit?]] |
| Standardbibliothek | [[lyx_-_programmiersprache:units|Standardbibliothek – Übersicht]] |
| Safety-Entwicklung | [[lyx_-_programmiersprache:guides:aerospace-safety|Aerospace & Safety]] |
Letzte Aktualisierung: 2026-08-13 · alle Beispiele geprüft mit ''lyxc 1.0.21A''; Kasten zu ''f64''-Array-Elementen aufgelöst (behoben, #1373), Kasten zu globalen Fließkomma-Schwellwerten auf ''con'' eingegrenzt