Lyx – Bedingungen (if / else / match)

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


1. Grundlegende Syntax

Eine if-Bedingung erwartet einen Ausdruck, der zu bool oder qbool evaluiert.

if (altitude < 1000) {
    ActivateLandingGear();
} else if (altitude > 40000) {
    ReduceThrust();
} else {
    MaintainFlight();
}

  • Klammern: Die Bedingung muss in runden Klammern stehen.
  • Blöcke: Geschweifte Klammern { } sind auch bei einzeiligen Anweisungen Pflicht.
  • Vergleich: Lyx verwendet = für Gleichheit (nicht ==) und := für Zuweisung. So ist eine versehentliche Zuweisung in einer Bedingung strukturell unmöglich.

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");
}


2. Logische Operatoren

Operator Bedeutung Beispiel
& UND (AND) a > 0 & b > 0
| ODER (OR) a = 0 | b = 0
! NICHT (NOT) !(a > 0)
!= Ungleich a != b
Lyx verwendet & und | — nicht && und ||. Beide Seiten werden immer ausgewertet (keine Short-Circuit-Evaluation per Default). Für Short-Circuit-Auswertung: Bedingungen in separate if-Blöcke verschachteln.

import std.io;

fn Check(x: int64, y: int64): void {
    // Beide Operanden werden immer ausgewertet
    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;
}

Nullable-Prüfung mit nil

Lyx verwendet nil (nicht null) für den Abwesenheitswert optionaler Typen:

// Optionaler Sensor — kann nil sein
var sensor: ^SensorData := nil;

// Erst auf nil prüfen, dann zugreifen
if (sensor != nil) {
    ProcessData(sensor^.value);
}

// Alternativ: Nil-Koaleszenz-Operator
var safe_sensor: ^SensorData := sensor ?? ^DefaultSensor;

In DAL-A/B-Code sollten optionale Pointer durch typsichere result-Rückgaben ersetzt werden — nil-Checks sind fehleranfällig und schwer zu analysieren.

3. Kurzform: if als Ausdruck

if kann als Ausdruck verwendet werden, wenn beide Zweige denselben Typ zurückgeben:

fn Abs(x: int64): int64 {
    return if (x < 0) { -x } else { x };
}

fn ClampLabel(v: int64): pchar {
    return if (v < 0) { "negativ" } else if (v = 0) { "null" } else { "positiv" };
}


4. Probabilistisches if (qbool)

Lyx unterstützt den Typ qbool für probabilistische Logik — z.B. für Energie-Aware-Entscheidungen oder Wahrscheinlichkeits-Simulationen.

// 1% Ausfallwahrscheinlichkeit
var failure_prob: qbool := 0.01q;

if (failure_prob) {
    // Dieser Block wird mit ~1% Wahrscheinlichkeit ausgeführt
    TriggerEmergencyRoutine();
}

qbool ist kein Ersatz für normale Bedingungen in Safety-kritischem Code — es ist ein Werkzeug für probabilistische Modellierung im Energy-Aware-Programmiermodell.

→ Ausführlich: Energy-Aware-Programmiermodell


5. Pattern Matching (match)

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(); }
    default            => { LogError(); }
}

  • Exhaustiveness: Der Compiler prüft (v0.9.0+), ob alle Enum-Werte abgedeckt sind. Fehlt ein Wert, ist es ein Compile-Fehler.
  • Kein Fallthrough: Im Unterschied zu C/Java gibt es kein implizites Durchfallen zwischen Fällen.
  • _ ist der Wildcard-Fall (entspricht default).

→ Vollständige Dokumentation: Pattern Matching


6. MC/DC und DO-178C Relevanz

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-instrument --coverage-report=cov.json src/flight.lyx
//
// Nach dem Testlauf Coverage-Report erzeugen:
// lyxc --coverage-report-html=report/ cov.json

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


7. Kein Präprozessor — Compile-Zeit-Bedingungen in Lyx

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-Berechnung schwieriger, weil der Compiler immer nur eine Variante des Codes sieht.

Stattdessen gibt es drei saubere Alternativen:

Alternative 1: con-Konstanten (Compile-Zeit-Auswertung)

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.

Alternative 2: Separate Unit-Dateien pro Plattform

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.

Alternative 3: @target-spezifische Attribute

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-06-05