Bedingungen steuern den Kontrollfluss in Lyx. Sie erlauben es, Codeblöcke nur dann auszuführen, wenn eine bestimmte logische Voraussetzung erfüllt ist.
→ Schleifen · Pattern Matching · Sprachsyntax
Eine if-Bedingung erwartet einen Ausdruck, der zu bool evaluiert.
if (altitude < 1000) {
ActivateLandingGear();
} else if (altitude > 40000) {
ReduceThrust();
} else {
MaintainFlight();
}
{ } sind auch bei einzeiligen Anweisungen Pflicht.== für Gleichheit und := für Zuweisung. In einer Bedingung wird ein einzelnes = abgewiesen — if (x = 0) ist ein Syntaxfehler. So ist eine versehentliche Zuweisung in einer Bedingung strukturell unmöglich. (Als eigenständige Anweisung wird x = 42; derzeit noch als Zuweisung übersetzt, siehe #1575.)
var speed: int64 := 250;
if (speed == 250) {
PrintLn("Reisegeschwindigkeit");
}
// Ungleich
if (speed != 0) {
PrintLn("Flugzeug bewegt sich");
}
// Kleiner/größer
if (speed > 300 | speed < 50) {
PrintLn("Außerhalb Normalbereich");
}
| Operator | Bedeutung | Auswertung | Beispiel |
|---|---|---|---|
&& | UND (AND) | Kurzschluss | a > 0 && b > 0 |
|| | ODER (OR) | Kurzschluss | a == 0 || b == 0 |
& | UND (AND) | beide Seiten | a > 0 & b > 0 |
| | ODER (OR) | beide Seiten | a == 0 | b == 0 |
! | NICHT (NOT) | – | !(a > 0) |
!= | Ungleich | – | a != b |
Kurzschlussauswertung: && und || werten die rechte Seite nur aus, wenn das Ergebnis noch nicht feststeht — && bricht bei falscher linker Seite ab, || bei wahrer. Das Ergebnis ist immer auf 0/1 normiert.
Damit funktioniert das Null-Guard-Idiom:
if (p != 0 && deref(p) > 0) { ... } // deref(p) läuft nur wenn p != 0
& und | werten dagegen immer beide Seiten aus. Sie sind die richtige Wahl für Bitmaskierung und Integer-Flags, nicht für Bedingungen mit Seiteneffekten oder Null-Prüfungen.
import std.io;
fn Check(x: int64, y: int64): void {
if (x > 0 && y > 0) {
PrintLn("beide positiv");
}
if (x < 0 || y < 0) {
PrintLn("mindestens eines negativ");
}
if (x != y) {
PrintLn("ungleich");
}
}
fn main(): int64 {
Check(3, 5);
Check(-1, 2);
Check(4, 4);
return 0;
}
Lyx verwendet null (nicht nil) für den Abwesenheitswert. Adressen werden als int64 geführt — einen Pointer-Typ gibt es nicht (siehe Syntax-Referenz, Abschnitt 4):
// Optionaler Sensor — 0 bedeutet "nicht vorhanden"
var sensor: int64 := 0;
// Erst prüfen, dann zugreifen
if (sensor != 0) {
ProcessData(peek64(sensor));
}
// Null-Koaleszenz-Operator
var safe_sensor: int64 := sensor ?? DefaultSensor();
In DAL-A/B-Code sollten optionale Adressen durch typsichereresult-Rückgaben ersetzt werden — Null-Prüfungen sind fehleranfällig und schwer zu analysieren.
Ein if als Ausdruck gibt es nicht — return if (x < 0) { -x } else { x }; ist ein Syntaxfehler, ebenso der ternäre Operator cond ? a : b. Stattdessen return in beiden Zweigen oder match:
fn Abs(x: int64): int64 {
if (x < 0) { return -x; }
return x;
}
fn ClampLabel(v: int64): pchar {
if (v < 0) { return "negativ"; }
if (v == 0) { return "null"; }
return "positiv";
}
match ist dagegen als Ausdruck verwendbar und oft die kompaktere Form:
fn DayName(day: int64): pchar {
return match (day) {
case 1 => "Montag";
case 2 => "Dienstag";
case _ => "Ungültig";
};
}
Einen eingebauten Typ qbool und Literale wie 0.01q gibt es nicht. Probabilistische Logik läuft über die Unit std.qbool und deren Struct-Typ QBool; die Auswertung zu einem echten bool erfolgt mit Observe():
import std.qbool;
var failure: QBool := QBoolNew(0.01); // 1 % Ausfallwahrscheinlichkeit
if (Observe(failure)) {
// wird mit ~1 % Wahrscheinlichkeit ausgefuehrt
TriggerEmergencyRoutine();
}
QBool ist kein Ersatz für normale Bedingungen in Safety-kritischem Code — es ist ein Werkzeug für probabilistische Modellierung.
→ Details: std.qbool · Datentypen, Abschnitt 6
Für Enum-Fallunterscheidungen und strukturierte Typen bietet Lyx match als typsichere Alternative zu langen if/else if-Ketten.
match (flight_state) {
case State.Idle => { PowerOn(); }
case State.Taxiing => { SetFlaps(10); }
case State.InAir => { RetractGear(); }
case _ => { LogError(); }
}
Auf–target=lyxoswähltmatchkeinen Zweig — auch nicht den Auffangfallcase _. Das Programm übersetzt ohne Meldung, läuft durch und gibt aus demmatchnichts aus (#1825, offen). Auflinux,arm64undriscvist das Verhalten unauffällig.
Wer für Lyx OS baut, benutzt bis dahinif/else if-Ketten — die tragen dort seitlyxc 1.1.8C(#1757).
case _ ⇒ ist der Wildcard-Fall. Ein default-Zweig wie in C/Java existiert nicht — default ist zwar ein reserviertes Wort, wird von match aber nicht akzeptiert.→ Vollständige Dokumentation: Pattern Matching
Neben match gibt es switch. Der Unterschied: switch vergleicht gegen Konstantenausdrücke, kennt einen default-Zweig und ist eine Anweisung — kein Ausdruck. Jeder Zweig ist ein Block.
import std.io;
fn main(): int64 {
var x: int64 := 2;
switch (x) {
case 1: { PrintLn("eins"); break; }
case 2: { PrintLn("zwei"); break; }
default: { PrintLn("andere"); break; }
}
return 0;
}
Jeder Zweig muss mit break oder return enden — auch der letzte und auch default. Fehlt es, meldet der Compiler:
sema error: switch case may fall through; add 'break' or 'return'
Das kehrt das Verhalten von C um: Dort fällt ein Zweig ohne break in den nächsten, und das vergessene break ist die häufigste Fehlerquelle des Konstrukts. In Lyx ist es umgekehrt ein Übersetzungsfehler.
Ein case-Label darf jeder zur Übersetzungszeit berechenbare Ausdruck sein — ein Literal, eine con-Konstante oder eine Rechnung daraus:
con GRENZE: int64 := 5;
switch (wert) {
case GRENZE: { PrintLn("an der Grenze"); break; }
case 2 * 3: { PrintLn("sechs"); break; }
default: { break; }
}
| Kriterium | switch | match |
|---|---|---|
| Als Ausdruck verwendbar | Nein | Ja — var r := match (x) { … } |
| Auffangfall | default: | case _ ⇒ |
| Bereiche | Nein | Ja — case 0..500 => |
| Guards | Nein | Ja — case 5 if x > 3 ⇒ |
| Zweig-Abschluss | break/return Pflicht | nicht nötig |
→ Pattern Matching — match im Detail
Für Luftfahrt-Zertifizierung (DAL A/B) reicht Branch-Coverage nicht aus — gefordert ist Modified Condition/Decision Coverage (MC/DC).
// Drei Bedingungen — für MC/DC braucht man 4 Testfälle (nicht 8)
fn IsLandingAllowed(gear_down: bool, speed_ok: bool, runway_clear: bool): bool {
return gear_down & speed_ok & runway_clear;
}
Der Compiler instrumentiert alle Bedingungen für MC/DC-Analyse:
// Build mit MC/DC-Instrumentierung:
// lyxc --mcdc-report src/flight.lyx -o /dev/null
// Achtung: zaehlt a && b als EINE Bedingung (#1524) — die Mindestzahl
// der Testfaelle ist von Hand herzuleiten
//
// Nach dem Testlauf Coverage-Report erzeugen:
// Eine HTML-Aufbereitung gibt es nicht (#1524)
Jede Teilbedingung in einem zusammengesetzten Ausdruck muss unabhängig das Gesamtergebnis beeinflussen können. Der Report zeigt welche Test-Vektoren dies nachweisen.
→ Vollständige Dokumentation: DO-178C Compliance
Lyx hat keinen Präprozessor und keine #ifdef/#if-Direktiven. Das ist eine bewusste Designentscheidung: Präprozessor-Konditionierung macht statische Analyse, MC/DC-Coverage und WCET (@wcet)-Berechnung schwieriger, weil der Compiler immer nur eine Variante des Codes sieht.
Stattdessen gibt es drei saubere Alternativen:
Konstante Ausdrücke mit con werden zur Compile-Zeit ausgewertet. Der Compiler eliminiert dead branches automatisch:
con DEBUG_BUILD: bool := false;
fn LogMessage(msg: pchar): void {
if (DEBUG_BUILD) {
Print(msg); // Compiler entfernt diesen Block im Release-Build vollständig
}
}
Der Unterschied zu #ifdef DEBUG: Der Code ist immer syntaktisch und typkorrekt geprüft — auch der „tote“ Zweig wird geparst und typgeprüft. Tippfehler in Debug-Code werden nicht erst beim Debug-Build entdeckt.
Für plattformspezifische Implementierungen werden separate Units angelegt und per Import ausgewählt:
src/
platform/
timer_linux.lyx // unit platform.timer; — Linux-Implementierung
timer_esp32.lyx // unit platform.timer; — ESP32-Implementierung
main.lyx
# Linux-Build
lyxc main.lyx -I src/platform/linux -o app
# ESP32-Build
lyxc main.lyx -I src/platform/esp32 --target=esp32 -o app.elf
Der import platform.timer; in main.lyx bleibt identisch — der –include-Pfad entscheidet, welche Implementierung genommen wird.
Architektur-spezifisches Verhalten wird über @section, @energy und @extern gesteuert — nicht über Textsubstitution:
// Immer korrekt — der Compiler wählt die passende Codegen-Strategie
@energy(1)
fn SleepMs(ms: int64): void {
// Auf ARM: WFI-Instruktion; auf x86: PAUSE + HLT
// Der Compiler kennt das --target und erzeugt plattformgerechten Code
}
| C-Präprozessor-Muster | Lyx-Äquivalent |
|---|---|
#ifdef DEBUG | con DEBUG: bool := false; + normale if-Bedingung |
#ifdef linux | Separate -I-Pfade pro Plattform |
#define MAX_SIZE 1024 | con MAX_SIZE: int64 := 1024; |
#ifdef ARM / #ifdef X86 | –target=arm64 / –target=linux + plattformspezifische Units |
#pragma once | Nicht nötig — jede .lyx-Datei ist eine Unit mit eindeutigem Namen |
Letzte Aktualisierung: 2026-08-27 — Hinweis zu match auf –target=lyxos ergänzt (#1825), mit lyxc 1.1.11B erhoben.
Codebeispiele geprüft: gegen lyxc 1.2.5C übersetzt (Prüflauf 2026-09-08 über die gesamte Doku: 574 Vollprogramme, 0 echte Fehler; zusätzlich 5159 Aufrufe gegen die pub fn-Signaturen in aurum/std gehalten, 0 Abweichungen).