====== 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. → [[lyx_-_programmiersprache:sprache:schleifen|Schleifen]] · [[lyx_-_programmiersprache:sprache:pattern-matching|Pattern Matching]] · [[lyx_-_programmiersprache:sprache:syntax|Sprachsyntax]] ---- ===== 1. Grundlegende Syntax ===== Eine ''if''-Bedingung erwartet einen Ausdruck, der zu ''bool'' 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 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 [[https://github.com/SEOLizer/LyX-Compiler/issues/1575|#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"); } ---- ===== 2. Logische Operatoren ===== ^ 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; } ==== Prüfung auf null ==== Lyx verwendet ''null'' (nicht ''nil'') für den Abwesenheitswert. Adressen werden als ''int64'' geführt — einen Pointer-Typ gibt es nicht (siehe [[lyx_-_programmiersprache:sprache:syntax|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 typsichere ''result''-Rückgaben ersetzt werden — Null-Prüfungen sind fehleranfällig und schwer zu analysieren. ---- ===== 3. Fallunterscheidung mit Rückgabewert ===== 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"; }; } ---- ===== 4. Probabilistische Entscheidungen ===== 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: [[lyx_-_programmiersprache:units:qbool|std.qbool]] · [[lyx_-_programmiersprache:sprache:datentypen|Datentypen, Abschnitt 6]] ---- ===== 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(); } case _ => { LogError(); } } > **Auf ''--target=lyxos'' wählt ''match'' keinen Zweig** — auch nicht den Auffangfall ''case _''. Das Programm übersetzt ohne Meldung, läuft durch und gibt aus dem ''match'' nichts aus ([[https://github.com/SEOLizer/LyX-Compiler/issues/1825|#1825]], offen). Auf ''linux'', ''arm64'' und ''riscv'' ist das Verhalten unauffällig. > > Wer für Lyx OS baut, benutzt bis dahin ''if''/''else if''-Ketten — die tragen dort seit ''lyxc 1.1.8C'' ([[https://github.com/SEOLizer/LyX-Compiler/issues/1757|#1757]]). * **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. * ''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: [[lyx_-_programmiersprache:sprache:pattern-matching|Pattern Matching]] ---- ===== 6. switch ===== 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; } ==== Kein Fall-through ==== **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. ==== Konstantenausdrücke als Label ==== 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; } } ==== switch oder match? ==== ^ 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 | → [[lyx_-_programmiersprache:sprache:pattern-matching|Pattern Matching — match im Detail]] ---- ===== 7. 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-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: [[lyx_-_programmiersprache:guides:do-178c|DO-178C Compliance]] ---- ===== 8. 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 ([[lyx_-_programmiersprache:sprache:attributes-pragmas|@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-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).